/***********************************************************************
 *
 *  ABLOGON.C
 *
 *
 *  The Microsoft At Work Fax Address Book Provider.
 *
 *  This file has the code to implement the Microsoft At Work Fax Address Book's logon
 *  object.
 *
 *  The following routines are implemented in this file:
 *
 *  ABPLOGON_QueryInterface
 *  ABPLOGON_Release
 *  ABPLOGON_Logoff
 *  ABPLOGON_OpenEntry
 *  ABPLOGON_CompareEntryIDs
 *  ABPLOGON_Advise
 *  ABPLOGON_Unadvise
 *  ABPLOGON_OpenStatusEntry
 *  ABPLOGON_OpenTemplateID
 *  ABPLOGON_GetOneOffTable
 *  ABPLOGON_PrepareRecips
 *
 *  LpMuidFromLogon
 *  HrLpszGetCurrentFileName
 *  HrReplaceCurrentfileName
 *  GenerateContainerDN
 *  HrBuildRootHier
 *
 *
 *  Copyright 1992, 1993, 1994 Microsoft Corporation.  All Rights Reserved.
 *
 *  Revision History:
 *
 *      When        Who                                 What
 *      --------    ------------------  ---------------------------------------
 *      8.3.94      MAPI                                Original source from MAPI sample ABP build 304
 *      8.3.94      Yoram Yaacovi               Moved code from the FAX ABP original abp.c to here
 *      11.11.94        Yoram Yaacovi           Upgrade to MAPI 318 (PR_INSTANCE_KEY)
 *
 ***********************************************************************/


#include "faxab.h"

ABPLOGON_Vtbl vtblABPLOGON =
{
    ABPLOGON_QueryInterface,
    (ABPLOGON_AddRef_METHOD *) ROOT_AddRef,
    ABPLOGON_Release,
    (ABPLOGON_GetLastError_METHOD *) ROOT_GetLastError,
    ABPLOGON_Logoff,
    ABPLOGON_OpenEntry,
    ABPLOGON_CompareEntryIDs,
    ABPLOGON_Advise,
    ABPLOGON_Unadvise,
    ABPLOGON_OpenStatusEntry,
    ABPLOGON_OpenTemplateID,
    ABPLOGON_GetOneOffTable,
    ABPLOGON_PrepareRecips
};



/*
 -  HrNewABLogon
 -
 *
 *  Creates a new Microsoft At Work Fax AB Logon object.
 */

HRESULT
HrNewABLogon( LPABLOGON *         lppABLogon,
              LPABPROVIDER        lpABP,
              LPMAPISUP           lpMAPISup,
              LPTSTR              lpszFABFile,
              LPMAPIUID           lpmuid,
              HINSTANCE           hLibrary,
              LPALLOCATEBUFFER    lpAllocBuff,
              LPALLOCATEMORE      lpAllocMore,
              LPFREEBUFFER        lpFreeBuff,
              LPMALLOC            lpMalloc
             )
{

    SCODE sc;
    HRESULT hResult = hrSuccess;
    SPropValue rgSPVStat[6];
    LPABPLOGON lpABPLogon = NULL;
#ifdef UNICODE
    CHAR szFileName[ MAX_PATH ];
#endif

    /*
     *  Allocate space for the lpABPLogon object
     */

    sc = lpAllocBuff(SIZEOF(ABPLOGON), &lpABPLogon);
    if (FAILED(sc))
    {
        hResult = ResultFromScode(sc);
        goto out;
    }

    /*
     *  Initialize the ABPLogon object
     */

    lpABPLogon->lpVtbl = &vtblABPLOGON;

    lpABPLogon->lcInit = 1;
    lpABPLogon->hResult = hrSuccess;
    lpABPLogon->idsLastError = 0;

    lpABPLogon->hLibrary = hLibrary;

    lpABPLogon->lpMalloc = lpMalloc;
    lpABPLogon->lpAllocBuff = lpAllocBuff;
    lpABPLogon->lpAllocMore = lpAllocMore;
    lpABPLogon->lpFreeBuff = lpFreeBuff;

    lpABPLogon->lpMapiSup = lpMAPISup;
    lpABPLogon->lpABP = (LPABPROVIDER) lpABP;
    lpABPLogon->lpszFileName = lpszFABFile;
    lpABPLogon->muidID = *lpmuid;

    lpABPLogon->lpTDatRoot = NULL;
    lpABPLogon->lpTDatOO = NULL;

    /*
     *  Register my status row...
     */

    // MAPI doesn't use UNICODE for this one...

    rgSPVStat[0].ulPropTag  = PR_DISPLAY_NAME_A;
#ifdef UNICODE
    szFileName[0] = 0;
    WideCharToMultiByte( CP_ACP, 0, lpszFABFile, -1, szFileName, ARRAYSIZE(szFileName), NULL, NULL );
    rgSPVStat[0].Value.lpszA = szFileName;
#else
    rgSPVStat[0].Value.lpszA = lpszFABFile;
#endif
    rgSPVStat[1].ulPropTag  = PR_RESOURCE_METHODS;
    rgSPVStat[1].Value.l    = 0;
    rgSPVStat[2].ulPropTag  = PR_RESOURCE_FLAGS;
    rgSPVStat[2].Value.l    = 0;
    rgSPVStat[3].ulPropTag  = PR_STATUS_CODE;
    rgSPVStat[3].Value.l    = STATUS_AVAILABLE;

    // MAPI doesn't use UNICODE for this one
    rgSPVStat[4].ulPropTag  = PR_STATUS_STRING_A;
    rgSPVStat[4].Value.lpszA = "Available";

    // MAPI doesn't use UNICODE for this one
    rgSPVStat[5].ulPropTag  = PR_PROVIDER_DISPLAY_A;
    rgSPVStat[5].Value.lpszA = "Microsoft Fax Address Book Provider";

    /*
     *  Set the Status Row for this provider,
     *  but do not allow an error from setting the
     *  status row to cause failure to Logon.
     */

    (void)lpMAPISup->lpVtbl->ModifyStatusRow(lpMAPISup,
        ARRAYSIZE(rgSPVStat), rgSPVStat, 0);

    /*
     *  AddRef the support object, because we're keeping
     *  a pointer to it in our Logon object.
     */
    lpMAPISup->lpVtbl->AddRef(lpMAPISup);

    /*
     *  AddRef our parent ABInit object
     */
    lpABP->lpVtbl->AddRef(lpABP);

    InitializeCriticalSection(&lpABPLogon->cs);

    *lppABLogon = (LPABLOGON) lpABPLogon;

out:

    DebugTraceResult(HrNewABPLogon, hResult);
    return hResult;
}


/*************************************************************************
 *
 -  ABPLOGON_QueryInterface
 -
 */
STDMETHODIMP
ABPLOGON_QueryInterface( LPABPLOGON lpABPLogon,
                         REFIID lpiid,
                         LPVOID * ppvObj
                        )
{
    if ( IsBadReadPtr(lpiid,   SIZEOF(IID)) ||
         IsBadWritePtr(ppvObj, SIZEOF(LPVOID))
        )
    {
        DebugTraceSc(ABPLOGON_QueryInterface, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

    /*  See if the requested interface is one of ours */

    if ( memcmp(lpiid, &IID_IUnknown, SIZEOF(IID)) &&
         memcmp(lpiid, &IID_IABLogon, SIZEOF(IID))
        )
    {
        *ppvObj = NULL;         /* OLE requires zeroing [out] parameter on error */
        DebugTraceSc(ABPLOGON_QueryInterface, E_NOINTERFACE);
        return ResultFromScode(E_NOINTERFACE);
    }

    /*  We'll do this one. Bump the usage count and return a new pointer. */

    EnterCriticalSection(&lpABPLogon->cs);
    ++lpABPLogon->lcInit;
    LeaveCriticalSection(&lpABPLogon->cs);

    *ppvObj = lpABPLogon;

    return hrSuccess;
}

/*
 *  Use ROOTs AddRef
 */

/*************************************************************************
 *
 -  ABPLOGON_Release
 -
 */
STDMETHODIMP_(ULONG)
ABPLOGON_Release(LPABPLOGON lpABPLogon)
{
    LONG lcInit;

    EnterCriticalSection(&lpABPLogon->cs);
    lcInit = --lpABPLogon->lcInit;
    LeaveCriticalSection(&lpABPLogon->cs);

    if (lcInit == 0)
    {
        DeleteCriticalSection(&lpABPLogon->cs);
        lpABPLogon->lpVtbl = NULL;
        lpABPLogon->lpFreeBuff(lpABPLogon);
        return (0);
    }
    return lcInit;
}

/*************************************************************************
 *
 -  ABPLOGON_Logoff
 -
 *  Logoff from this logon object.  Clean up any resources/objects that
 *  our logon object has accumulated.
 *
 *
 */
STDMETHODIMP
ABPLOGON_Logoff(LPABPLOGON lpABPLogon, ULONG ulFlags)
{

    DebugTrace("AWFXAB32(ABPLOGON_Logoff): entering\n");

#ifdef DO_WE_REALLY_NEED_TAPI
    /*
     * Let TAPI go
     */
    DeinitTAPI();
#endif

    /*
     *  Remove this logon object from the list of known
     *  logon objects associated with this initialization
     *  of this provider.
     */
    (void) RemoveLogonObject(lpABPLogon->lpABP, lpABPLogon, lpABPLogon->lpFreeBuff);

    /*
     *  No longer need to be holding on to our parent
     */
    lpABPLogon->lpABP->lpVtbl->Release(lpABPLogon->lpABP);

    /*
     *  Free up the file
     */
    lpABPLogon->lpFreeBuff(lpABPLogon->lpszFileName);

    if (lpABPLogon->lpTDatRoot)
        lpABPLogon->lpTDatRoot->lpVtbl->Release(lpABPLogon->lpTDatRoot);

    if (lpABPLogon->lpTDatOO)
        lpABPLogon->lpTDatOO->lpVtbl->Release(lpABPLogon->lpTDatOO);

    /*
     *  very last thing I should do is release the support object
     */
    lpABPLogon->lpMapiSup->lpVtbl->Release(lpABPLogon->lpMapiSup);

        DebugTrace("AWFXAB32(ABPLOGON_Logoff): leaving\n");

    return hrSuccess;
}

/*************************************************************************
 *
 -  ABPLOGON_OpenEntry
 -
 *  Creates an object with (at least) the IMAPIProp interface from an
 *  entryID.
 *
 *  There are four valid types of entryIDs handled:
 *
 *    NULL          <- return back the root container object
 *    DIR_ENTRYID   <- return back the directory container object
 *    USR_ENTRYID   <- return back the MAILUSER object
 *    OOUSER_ENTRYID <- return back the OneOff MAILUSER object
 *
 *  Note:  This call is reused for all other internal objects that support OpenEntry().
 *    Those other calls *must* check their parameters before calling this method.
 *    The only other way this method is called is via MAPI which does parameter checking
 *    for us.  The most we'll do here is assert our parameters.
 */
STDMETHODIMP
ABPLOGON_OpenEntry( LPABPLOGON lpABPLogon,
                    ULONG cbEntryID,
                    LPENTRYID lpEntryID,
                    LPCIID lpInterface,
                    ULONG ulFlags,
                    ULONG * lpulObjType,
                    LPUNKNOWN * lppUnk
                   )
{

    LPDIR_ENTRYID lpEID = (LPDIR_ENTRYID) lpEntryID;
    HRESULT hResult = hrSuccess;
    LPTSTR lpszFileName;

    /*
     *  Check the EntryID
     */

    // used to be: if (!lpEntryID)
    if (!cbEntryID)
    {
        LPABCONT lpABCont = NULL;

        /*
         *  Special case:  the root level object
         */

        NFAssertSz(!lpEntryID, "Non-NULL entry id passed with 0 cb to OpenEntry()\n");

        /*  Make this new object  */

        /*
        *  Get the current .FAB file name from our logon object
        */
        hResult = HrLpszGetCurrentFileName((LPABLOGON) lpABPLogon, &lpszFileName);
        if (HR_FAILED(hResult))
            goto out;

        // If there is a real Fax AB
        if ((fExposeFaxAB) &&
                (lpszFileName[0] != 0))

        hResult = HrNewROOT((LPABCONT *) lppUnk,
                    lpulObjType,
                    (LPABLOGON) lpABPLogon,
                    lpInterface,
                    lpABPLogon->hLibrary,
                    lpABPLogon->lpAllocBuff,
                    lpABPLogon->lpAllocMore,
                    lpABPLogon->lpFreeBuff,
                    lpABPLogon->lpMalloc);

        else
                // No Fax AB container
                return ResultFromScode (MAPI_E_INTERFACE_NOT_SUPPORTED);

        lpABPLogon->lpFreeBuff(lpszFileName);
        goto out;
    }

    /*
     *  There's an entryID there, is it mine??
     *  I need to check because I'm reusing this routine for
     *  my Container->OpenEntry call, and I can't be sure the
     *  client will always be well behaved.
     *
     *  When this routine is called from MAPI, this call is redundant.  But
     *  because I'm reusing this routine, I gotta check.
     */

    /*  Compare MAPIUIDs  */
    if (memcmp(&(((LPDIR_ENTRYID) lpEntryID)->muid), &muidABMAWF,
            SIZEOF(MAPIUID)))
    {
        /*
         *  Not mine!
         */

        hResult = ResultFromScode(MAPI_E_INVALID_ENTRYID);
        DebugTraceResult(ABPLOGON_OpenEntry, hResult);
        goto out;
    }

    /*
     *  What object does this correspond to??
     */

    /*  I've only got two types: containers and users  */

    if (lpEID->ulType == MAWF_DIRECTORY)
    {
        LPABLOGON lpABPLogonT = NULL;

        /* entry id must have the same verson number */
        if (lpEID->ulVersion != MAWF_VERSION)
        {
            hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID);
            SetErrorIDS(lpABPLogon, hResult, IDS_OLD_EID);

            goto out;
        }

        /*
         *  find the correct logon object for this entryid
         */

        (void) FindLogonObject(lpABPLogon->lpABP, &lpEID->muidID, &lpABPLogonT);

        /* did we find the corresponding logon object */
        if (!lpABPLogonT)
        {
            hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID);
            goto out;
        }

        // If I don't have a fab file at this point, I can't open the AB container
        HrLpszGetCurrentFileName((LPABLOGON) lpABPLogonT, &lpszFileName);
        if (lpszFileName[0] == 0)
        {
            hResult = ResultFromScode (MAPI_E_NO_SUPPORT);
            DebugTraceResult(ABPLOGON_OpenEntry, hResult);
            goto out;
        }

        hResult = HrNewFaxDirectory( (LPABCONT *) lppUnk,
                                    lpulObjType,
                                    (LPABLOGON) lpABPLogonT,
                                    lpInterface,
                                    lpABPLogon->hLibrary,
                                    lpABPLogon->lpAllocBuff,
                                    lpABPLogon->lpAllocMore,
                                    lpABPLogon->lpFreeBuff,
                                    lpABPLogon->lpMalloc);
        goto out;

    }

    if (lpEID->ulType == MAWF_USER)
    {
        if (cbEntryID == (ULONG) sizeof(USR_ENTRYID))
        {
            hResult = HrNewFaxUser( (LPMAILUSER *) lppUnk,
                                    lpulObjType,
                                    cbEntryID,
                                    lpEntryID,
                                    (LPABLOGON) lpABPLogon,
                                    lpInterface,
                                    lpABPLogon->hLibrary,
                                    lpABPLogon->lpAllocBuff,
                                    lpABPLogon->lpAllocMore,
                                    lpABPLogon->lpFreeBuff,
                                    lpABPLogon->lpMalloc);

            goto out;
        }
    }


    if (lpEID->ulType == MAWF_ONEOFF)
    {
        if (cbEntryID == (ULONG) sizeof(OOUSER_ENTRYID))
        {
            hResult = HrNewFaxOOUser( (LPMAILUSER *) lppUnk,
                                        lpulObjType,
                                        cbEntryID,
                                        lpEntryID,
                                        (LPABLOGON) lpABPLogon,
                                        lpInterface,
                                        lpABPLogon->hLibrary,
                                        lpABPLogon->lpAllocBuff,
                                        lpABPLogon->lpAllocMore,
                                        lpABPLogon->lpFreeBuff,
                                        lpABPLogon->lpMalloc);


            goto out;
        }
    }

    hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID);

out:
    DebugTraceResult(ABPLOGON_OpenEntry, hResult);

    return hResult;

}

/*************************************************************************
 *
 -  ABPLOGON_CompareEntryIDs
 -
 *  If the two entryids are mine and they're of the same type, then
 *  just do a binary comparison to see if they're equal.
 *
 */
STDMETHODIMP
ABPLOGON_CompareEntryIDs( LPABPLOGON lpABPLogon,
                          ULONG cbEntryID1,
                          LPENTRYID lpEntryID1,
                          ULONG cbEntryID2,
                          LPENTRYID lpEntryID2,
                          ULONG ulFlags,
                          ULONG * lpulResult
                         )
{

    LPDIR_ENTRYID lpEID1 = (LPDIR_ENTRYID) lpEntryID1;
    LPDIR_ENTRYID lpEID2 = (LPDIR_ENTRYID) lpEntryID2;
    HRESULT hResult = hrSuccess;

    /*
     *  Check to see if their MUID is mine
     */
    if ( memcmp(&(lpEID1->muid), &muidABMAWF, SIZEOF(MAPIUID)) ||
         memcmp(&(lpEID2->muid), &muidABMAWF, SIZEOF(MAPIUID))
        )
    {
        /*
         *  No recognition of these entryids.
         */

        *lpulResult = (ULONG) FALSE;
        hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID);
        goto out;
    }

    /*
     *  See if the type of entryids are the same
     */
    if (lpEID1->ulType != lpEID2->ulType)
    {
        /*
         *  They're not, so they don't match
         */

        *lpulResult = (ULONG) FALSE;
        goto out;

    }

    /*
     *  See if the entryids are the same size.  They'd better be
     *  if they're the same type.
     */
    if (cbEntryID1 != cbEntryID2)
    {
        /*
         *  They're not?!?  Then I don't know these...
         */

        *lpulResult = (ULONG) FALSE;
        hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID);

        goto out;
    }

    /*
     *  Check for Directory entryids
     */
    if (lpEID1->ulType == MAWF_DIRECTORY)
    {
        /*
         *  Ok, I'm dealing with directory entryids
         */

        /*
         *  Better make sure it's the right size
         */
        if (cbEntryID1 != sizeof(DIR_ENTRYID))
        {
            /*
             *  This doesn't make sense.  I don't recognize this entryid.
             */

            *lpulResult = (ULONG) FALSE;
            hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID);

            goto out;
        }

        /*
         *  At this point it's just a memcmp
         */
        if (memcmp(lpEID1, lpEID2, SIZEOF(DIR_ENTRYID)))
        {
            /*
             *  They're not equal
             */

            *lpulResult = (ULONG) FALSE;

            goto out;
        }

        /*
         *  They must be the same
         */

        *lpulResult = (ULONG) TRUE;

        goto out;
    }

    if (lpEID1->ulType == MAWF_USER)
    {
        /*
         *  Ok, I'm dealing with user entryids
         */

        /*
         *  Better make sure it's the right size
         */
        if (cbEntryID1 != sizeof(USR_ENTRYID))
        {
            /*
             *  This doesn't make sense.  I don't recognize this entryid.
             */

            *lpulResult = (ULONG) FALSE;
            hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID);

            goto out;
        }

        /*
         *  At this point it's just a memcmp
         */
        if (memcmp(lpEID1, lpEID2, SIZEOF(USR_ENTRYID)))
        {
            /*
             *  They're not equal
             */

            *lpulResult = (ULONG) FALSE;

            goto out;
        }

        /*
         *  They must be the same
         */

        *lpulResult = (ULONG) TRUE;

        goto out;
    }

    if (lpEID1->ulType == MAWF_ONEOFF)
    {
        /*
         *  Ok, I'm dealing with oneoff user entryids
         */

        /*
         *  Better make sure it's the right size
         */
        if (cbEntryID1 != SIZEOF(OOUSER_ENTRYID))
        {
            /*
             *  This doesn't make sense.  I don't recognize this entryid.
             */

            *lpulResult = (ULONG) FALSE;
            hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID);

            goto out;
        }

        /*
         *  At this point it's just a memcmp
         */
        if (memcmp(lpEID1, lpEID2, SIZEOF(OOUSER_ENTRYID)))
        {
            /*
             *  They're not equal
             */

            *lpulResult = (ULONG) FALSE;

            goto out;
        }

        /*
         *  They must be the same
         */

        *lpulResult = (ULONG) TRUE;

        goto out;
    }

    /*
     *  It's no entryid I know of
     */

    *lpulResult = (ULONG) FALSE;
    hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID);

out:

    DebugTraceResult(ABPLOGON_CompareEntryIDs, hResult);
    return hResult;

}

/*************************************************************************
 *
 -  ABPLOGON_OpenStatusEntry
 -
 *
 *
 *
 */
STDMETHODIMP
ABPLOGON_OpenStatusEntry( LPABPLOGON lpABPLogon,
                          LPCIID lpIID,
                          ULONG ulFlags,
                          ULONG FAR * lpulObjType,
                          LPMAPISTATUS FAR * lppEntry
                         )
{
    HRESULT hr;

    /*
     *  Validate Parameters
     */
    if ( IsBadReadPtr(lpABPLogon,      (UINT) SIZEOF(ABPLOGON))    ||
         (lpIID && IsBadReadPtr(lpIID, (UINT) SIZEOF(IID)))        ||
         IsBadWritePtr(lpulObjType,    (UINT) SIZEOF(ULONG FAR *)) ||
         IsBadWritePtr(lppEntry,       (UINT) SIZEOF(LPMAPISTATUS))
        )
    {
        DebugTraceSc(ABPLogon_OpenStatusEntry, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }


    hr = HrNewStatusObject( lppEntry,
                            lpulObjType,
                            ulFlags,
                            (LPABLOGON) lpABPLogon,
                            lpIID,
                            lpABPLogon->hLibrary,
                            lpABPLogon->lpAllocBuff,
                            lpABPLogon->lpAllocMore,
                            lpABPLogon->lpFreeBuff,
                            lpABPLogon->lpMalloc);

    DebugTraceResult(ABPLOGON_OpenStatusEntry, hr);
    return hr;
}

/*************************************************************************
 *
 -  ABPLOGON_OpenTemplateID
 -
 *
 *
 *
 */
STDMETHODIMP
ABPLOGON_OpenTemplateID( LPABPLOGON lpABPLogon,
                         ULONG cbTemplateId,
                         LPENTRYID lpTemplateId,
                         ULONG ulTemplateFlags,
                         LPMAPIPROP lpMAPIPropData,
                         LPCIID lpInterface,
                         LPMAPIPROP * lppMAPIPropNew,
                         LPMAPIPROP lpMAPIPropSibling
                        )
{
    HRESULT hResult;

    /*
     *  Validate Parameters
     */
    if ( IsBadReadPtr(lpABPLogon,      (UINT) SIZEOF(ABPLOGON))   ||
         IsBadReadPtr(lpTemplateId,    (UINT) cbTemplateId)       ||
         IsBadReadPtr(lpMAPIPropData,  (UINT) SIZEOF(LPVOID))     ||
         (lpInterface && IsBadReadPtr(lpInterface, (UINT) sizeof(IID))) ||
         IsBadWritePtr(lppMAPIPropNew, (UINT) SIZEOF(LPMAPIPROP)) ||
         (lpMAPIPropSibling && IsBadReadPtr(lpMAPIPropSibling, (UINT) SIZEOF(LPVOID)))
        )
    {
        DebugTraceSc(ABPLogon_OpenTemplateID, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

    /* //$ need stronger checking here... */
    /* entryid better be right size */
    if (cbTemplateId != sizeof(OOUSER_ENTRYID) && cbTemplateId != sizeof(USR_ENTRYID))
    {
        hResult = ResultFromScode(MAPI_E_INVALID_ENTRYID);
        goto out;
    }

    /*  is it my entry id compare MAPIUIDs  */
    if (memcmp(&(((LPUSR_ENTRYID) lpTemplateId)->muid), &muidABMAWF, SIZEOF(MAPIUID)))
    {
       /*
        *  Not mine!
        */
        hResult = ResultFromScode( MAPI_E_INVALID_ENTRYID );
        goto out;
    }

    /* better be a oneoff user entryid or a user entry id */
    if (((LPUSR_ENTRYID) lpTemplateId)->ulType == MAWF_ONEOFF)
    {

        hResult = HrNewOOTID( lppMAPIPropNew,
                              cbTemplateId,
                              lpTemplateId,
                              ulTemplateFlags,
                              lpMAPIPropData,
                              (LPABLOGON) lpABPLogon,
                              lpInterface,
                              lpABPLogon->hLibrary,
                              lpABPLogon->lpAllocBuff,
                              lpABPLogon->lpAllocMore,
                              lpABPLogon->lpFreeBuff,
                              lpABPLogon->lpMalloc
                             );

    }
    else if (((LPUSR_ENTRYID) lpTemplateId)->ulType == MAWF_USER)
    {
        hResult = HrNewTID( lppMAPIPropNew,
                            cbTemplateId,
                            lpTemplateId,
                            ulTemplateFlags,
                            lpMAPIPropData,
                            (LPABLOGON) lpABPLogon,
                            lpInterface,
                            lpABPLogon->hLibrary,
                            lpABPLogon->lpAllocBuff,
                            lpABPLogon->lpAllocMore,
                            lpABPLogon->lpFreeBuff,
                            lpABPLogon->lpMalloc
                           );
    }
    else
    {
        hResult = MakeResult(MAPI_E_INVALID_ENTRYID);
    }

out:

    DebugTraceResult(ABPLOGON_OpenTemplateID, hResult);
    return hResult;
}

/*
 -  ABPLOGON_GetOneOffTable
 -
 *  Returns the lists of one-offs that this providers can support creation of.
 *  This list is added to the entries gathered from all the other AB logon objects
 *  and exposed to the user as the list of things that can be created on a
 *  message.  Also this total list is available to other providers through the
 *  support method GetOneOffTable().
 *
 *  Note:  There's a bug here that if there are more than one Microsoft At Work Fax Address Books
 *  installed on a particular profile, then there will be multiple entries in the
 *  one-off table from this provider.  This can be changed to only have one one-off
 *  entry, no matter how many FABs are configured in a profile, if the one-off table
 *  was associated with the ABInit object.
 */

/*
 *  Column set for the oneoff table
 */
enum {  ivalootPR_DISPLAY_NAME = 0,
        ivalootPR_ENTRYID,
        ivalootPR_DEPTH,
        ivalootPR_SELECTABLE,
        ivalootPR_ADDRTYPE,
        ivalootPR_DISPLAY_TYPE,
        ivalootPR_INSTANCE_KEY,
        ivalootMax };

static const SizedSPropTagArray(ivalootMax, tagaColSetOOTable) =
{
    ivalootMax,
    {
        PR_DISPLAY_NAME_A,
        PR_ENTRYID,
        PR_DEPTH,
        PR_SELECTABLE,
        PR_ADDRTYPE_A,
        PR_DISPLAY_TYPE,
        PR_INSTANCE_KEY
    }
};

STDMETHODIMP
ABPLOGON_GetOneOffTable( LPABPLOGON lpABPLogon,
                         ULONG ulFlags,
                         LPMAPITABLE * lppTable
                        )
{
    SCODE       sc;
    HRESULT     hResult;
    SRow        sRow;
    SPropValue  rgsPropValue[ivalootMax];
    CHAR        displayNameString[MAX_DISPLAY_NAME];
    ULONG       ulInstanceKey = 1;
    HINSTANCE   hInst;
#ifdef UNICODE
    CHAR        szEMT[ MAX_PATH ];
#endif

    /*
     *  Validate Parameters
     */

    if ( ulFlags & ~(MAPI_UNICODE) )
    {
        DebugTraceArg( APBLOGON_GetOneOffTable, "Unknown Flags" );
        return ResultFromScode( MAPI_E_UNKNOWN_FLAGS );
    }

    if ( ulFlags & MAPI_UNICODE )
    {
        DebugTraceArg( APBLOGON_GetOneOffTable, "UNICODE not supported" );
        return ResultFromScode( MAPI_E_BAD_CHARWIDTH );
    }

    if (    IsBadReadPtr(lpABPLogon, (UINT) SIZEOF(ABPLOGON))
         || IsBadWritePtr(lppTable,  (UINT) SIZEOF(LPMAPITABLE))
        )
    {
        DebugTraceSc(ABPLogon_GetOneOffTable, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }


    EnterCriticalSection(&lpABPLogon->cs);

    /*
     * If there's not one already associated with this logon object,
     * then create one.
     */
    if (!lpABPLogon->lpTDatOO)
    {
        /* Create a Table data object */
        sc = CreateTable(
            (LPIID) &IID_IMAPITableData,
            lpABPLogon->lpAllocBuff,
            lpABPLogon->lpAllocMore,
            lpABPLogon->lpFreeBuff,
            lpABPLogon->lpMalloc,
            0,
            PR_DISPLAY_NAME_A,
            (LPSPropTagArray) &tagaColSetOOTable,
            &(lpABPLogon->lpTDatOO));

        if (FAILED(sc))
        {
            hResult = ResultFromScode(sc);
            goto out;
        }


        // Get the instance handle, so that I can get the display strings off the resource file
        hInst = lpABPLogon->hLibrary;

        // Initialize the row
        sRow.cValues = ivalootMax;
        sRow.lpProps = rgsPropValue;

        /*
         *      Fill the table
         *
         *      we want to add two entries to the one-off table, so that we'll get:
         *
         *      Microsoft Fax
         *              Fax
         */

        // First do the 'Microsoft Fax'

        // Name of the One-Off
        rgsPropValue[ivalootPR_DISPLAY_NAME].ulPropTag  = PR_DISPLAY_NAME_A;
        LoadStringA( hInst, IDS_MAWF_NAME, displayNameString, ARRAYSIZE(displayNameString));
        rgsPropValue[ivalootPR_DISPLAY_NAME].Value.lpszA = displayNameString;

        rgsPropValue[ivalootPR_ENTRYID].ulPropTag     = PR_ENTRYID;
        rgsPropValue[ivalootPR_ENTRYID].Value.bin.cb  = SIZEOF(OOUSER_ENTRYID);
        rgsPropValue[ivalootPR_ENTRYID].Value.bin.lpb = (LPVOID) &ONEOFF_EID;

        // the hierarcy level (how far to indent a display name). I choose not to indent
        rgsPropValue[ivalootPR_DEPTH].ulPropTag = PR_DEPTH;
        rgsPropValue[ivalootPR_DEPTH].Value.l = 0;

        // Selection flags. TRUE indicates this entry ID can be used in CreateEntry() call.
        rgsPropValue[ivalootPR_SELECTABLE].ulPropTag = PR_SELECTABLE;
        rgsPropValue[ivalootPR_SELECTABLE].Value.b = TRUE;

        //  The address type that would be generated by an entry
        //  created from this template
        rgsPropValue[ivalootPR_ADDRTYPE].ulPropTag = PR_ADDRTYPE_A;
#ifdef UNICODE
        szEMT[0] = 0;
        WideCharToMultiByte( CP_ACP, 0, lpszEMT, -1, szEMT, ARRAYSIZE(szEMT), NULL, NULL );
        rgsPropValue[ivalootPR_ADDRTYPE].Value.lpszA = szEMT;
#else
        rgsPropValue[ivalootPR_ADDRTYPE].Value.LPSZ = lpszEMT;
#endif

        /*
         *  The display type associated with a recipient built with this template
         */
        rgsPropValue[ivalootPR_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE;
        rgsPropValue[ivalootPR_DISPLAY_TYPE].Value.lpszA = DT_MAILUSER;

        /*
         *  The instance key of this row in this one-off table.
         *  using 1 for this row
         */
        ulInstanceKey = 1;
        rgsPropValue[ivalootPR_INSTANCE_KEY].ulPropTag     = PR_INSTANCE_KEY;
        rgsPropValue[ivalootPR_INSTANCE_KEY].Value.bin.cb  = SIZEOF(ULONG);
        rgsPropValue[ivalootPR_INSTANCE_KEY].Value.bin.lpb = (LPBYTE) &ulInstanceKey;

        (void) lpABPLogon->lpTDatOO->lpVtbl->HrModifyRow(
                            lpABPLogon->lpTDatOO,
                            &sRow);

#ifdef DO_MULTI_LEVEL_ADDRESS_BOOK_STUFF
        // Now do the 'Fax'

        // Name of the One-Off
        rgsPropValue[ivalootPR_DISPLAY_NAME].ulPropTag    = PR_DISPLAY_NAME_A;
        LoadStringA(hInst, IDS_FAX_NAME, displayNameString, ARRAYSIZE(displayNameString));
        rgsPropValue[ivalootPR_DISPLAY_NAME].Value.lpszA  = displayNameString;

        // The entry ID for this template. MAPI will call OpenEntry() with this entry ID
        // RtlZeroMemory(&EntryID, sizeof(OOUSER_ENTRYID));

        rgsPropValue[ivalootPR_ENTRYID].ulPropTag     = PR_ENTRYID;
        rgsPropValue[ivalootPR_ENTRYID].Value.bin.cb  = SIZEOF(OOUSER_ENTRYID);
        rgsPropValue[ivalootPR_ENTRYID].Value.bin.lpb = (LPVOID) &ONEOFF_EID;

        // the hierarcy level (how far to indent a display name). I choose not to indent
        rgsPropValue[ivalootPR_DEPTH].ulPropTag = PR_DEPTH;
        rgsPropValue[ivalootPR_DEPTH].Value.l = 1;

        // Selection flags. TRUE indicates this entry ID can be used in CreateEntry() call.
        rgsPropValue[ivalootPR_SELECTABLE].ulPropTag = PR_SELECTABLE;
        rgsPropValue[ivalootPR_SELECTABLE].Value.b = TRUE;

        //  The address type that would be generated by an entry
        //  created from this template
        rgsPropValue[ivalootPR_ADDRTYPE].ulPropTag  = PR_ADDRTYPE_A;
#ifdef UNICODE
        rgsPropValue[ivalootPR_ADDRTYPE].Value.lpszA = szEMT;
#else
        rgsPropValue[ivalootPR_ADDRTYPE].Value.LPSZ = lpszEMT;
#endif

        /*
         *  The display type associated with a recipient built with this template
         */
        rgsPropValue[ivalootPR_DISPLAY_TYPE].ulPropTag   = PR_DISPLAY_TYPE;
        rgsPropValue[ivalootPR_DISPLAY_TYPE].Value.lpszA = DT_MAILUSER;

        /*
         *  The instance key of this row in this one-off table.
         *  using 2 for this row
         */
        ulInstanceKey = 2;
        rgsPropValue[ivalootPR_INSTANCE_KEY].ulPropTag = PR_INSTANCE_KEY;
        rgsPropValue[ivalootPR_INSTANCE_KEY].Value.bin.cb = SIZEOF(ULONG);
        rgsPropValue[ivalootPR_INSTANCE_KEY].Value.bin.lpb = (LPBYTE) &ulInstanceKey;

        (void) lpABPLogon->lpTDatOO->lpVtbl->HrModifyRow(
                            lpABPLogon->lpTDatOO,
                            &sRow);

#endif
    }

    /*
     *  Get a view to return to the caller
     */
    hResult = lpABPLogon->lpTDatOO->lpVtbl->HrGetView(
        lpABPLogon->lpTDatOO,
        NULL,
        NULL,
        0,
        (LPMAPITABLE *) lppTable);

out:

    LeaveCriticalSection(&lpABPLogon->cs);

    DebugTraceResult(ABPLogon_GetOneOffTable, hResult);
    return hResult;
}

/*************************************************************************
 *
 -  ABPLOGON_Advise
 -
 *  NYI
 *
 *
 */
STDMETHODIMP
ABPLOGON_Advise( LPABPLOGON lpABPLogon,
                 ULONG cbEntryID,
                 LPENTRYID lpEntryID,
                 ULONG ulEventMask,
                 LPMAPIADVISESINK lpAdviseSink,
                 ULONG FAR * lpulConnection
                )
{
    DebugTraceSc(ABPLOGON_Advise, MAPI_E_NO_SUPPORT);
    return ResultFromScode(MAPI_E_NO_SUPPORT);
}

/*************************************************************************
 *
 -  ABPLOGON_Unadvise
 -
 *  NYI
 *
 *
 */
STDMETHODIMP
ABPLOGON_Unadvise(LPABPLOGON lpABPLogon, ULONG ulConnection)
{
    DebugTraceSc(ABPLOGON_Unadvise, MAPI_E_NO_SUPPORT);
    return ResultFromScode(MAPI_E_NO_SUPPORT);
}

/*************************************************************************
 *
 -  ABPLOGON_PrepareRecips
 -
 *      Takes a list of recipients and sets values for a requested list of
 *      properties (lpPropTagArray) on each of the recipients, from the respective
 *      address book entry of the recipient. If the recipient already has
 *      some properties set on it, those that exist on the address book entry
 *      will override the one that are on the recipient. Those that do NOT
 *      exist in the address book entry will stay.
 *
 *
 */
STDMETHODIMP
ABPLOGON_PrepareRecips( LPABPLOGON lpABPLogon,
                        ULONG           ulFlags,
                        LPSPropTagArray lpPropTagArray,
                        LPADRLIST       lpRecipList
                       )
{
    HRESULT         hResult         = hrSuccess;
    UINT            iRecip;
    UINT            iProp;
    ULONG           cValues;
    LPSPropValue    lpspvUser       = NULL;
    LPSPropValue    lpNewRecip      = NULL;
    LPMAPIPROP      lpMAPIPropEntry = NULL;
    SCODE           sc              = S_OK;
    ULONG           ulObjType;
    BOOL            fUselpspvUser;

    if (!lpPropTagArray)
    {
        /*
         *  They only want us to update our entryID from ephemeral to
         *  permanent.  Since ours are already permanent, we don't need to
         *  do anything.
         */
        goto out;
    }

    /* loop through all the recipients */

    for (iRecip = 0; iRecip < lpRecipList->cEntries; iRecip++)
    {
        LPUSR_ENTRYID   lpEntryID       = NULL;
        ULONG           cbEntryID;
        LPSPropValue    lpPropVal       = NULL;
        LPSPropValue    rgpropvalsRecip = lpRecipList->aEntries[iRecip].rgPropVals;
        ULONG           cPropsRecip     = lpRecipList->aEntries[iRecip].cValues;

        /* For each recipient, find its entryid */

        lpPropVal = PpropFindProp( rgpropvalsRecip, cPropsRecip, PR_ENTRYID );

        if ( lpPropVal )
        {
            lpEntryID = (LPUSR_ENTRYID)lpPropVal->Value.bin.lpb;
            cbEntryID = lpPropVal->Value.bin.cb;
        }
        else
            continue;

        /* Is it one of ours? */

        if ( cbEntryID < CbNewENTRYID(0)
            || IsBadReadPtr( (LPVOID) lpEntryID, (UINT) cbEntryID ) )
        {
            continue;   /* no, keep looking */
        }

        if ( memcmp( &(lpEntryID->muid), &muidABMAWF, SIZEOF(MAPIUID) ) )
            continue;   /* no, keep looking */

        /* Try and open it. */

        hResult = HrNewFaxUser( (LPMAILUSER *)&lpMAPIPropEntry,
                                &ulObjType,
                                cbEntryID,
                                (LPENTRYID) lpEntryID,
                                (LPABLOGON) lpABPLogon,
                                NULL,
                                lpABPLogon->hLibrary,
                                lpABPLogon->lpAllocBuff,
                                lpABPLogon->lpAllocMore,
                                lpABPLogon->lpFreeBuff,
                                lpABPLogon->lpMalloc
                               );

        if ( HR_FAILED(hResult) )
        {
             /* Couldn't open it...; Ignore it and keep looking */

            hResult = hrSuccess;
            DebugTrace( "ABPLOGON_PrepareRecips sees a bad user entry ID\n" );
            continue;
        }

        /* Get the properties requested */

        hResult = lpMAPIPropEntry->lpVtbl->GetProps( lpMAPIPropEntry,
                lpPropTagArray,
                0, /* ansi */
                &cValues, &lpspvUser );

        /* No longer need lpMAPIPropEntry  */

        lpMAPIPropEntry->lpVtbl->Release(lpMAPIPropEntry);
        lpMAPIPropEntry = NULL;

        if (HR_FAILED(hResult))
        {
            /* Failed getting properties. Cleanup and ignore this entry */

            hResult = hrSuccess;
            continue;
        }

        hResult = hrSuccess;

        Assert(cValues == lpPropTagArray->cValues);

        /*
         *  This is the hard part.
         *  Merge the two property sets: lpspvUser and lpsPropVal.  Note that
         *  both of these sets may have the same property - chances are they do.
         *  for these conflicts, lpspvUser should be the one we get the property
         *  from.
         *
         *  Guess how big the resultant SPropValue array is, and allocate one of that
         *  size.
         */

        sc = lpABPLogon->lpAllocBuff( (cValues + cPropsRecip) * SIZEOF( SPropValue ),
                &lpNewRecip);
        if (FAILED(sc))
        {
            /*
             *  Ok, to fail the call here.  If we're running into out of memory conditions
             *  we're all in trouble.
             */

            hResult = ResultFromScode( sc );
            goto err;
        }

        /*
         *  Copy lpspvUser properties over to lpNewRecip
         *  Check each property in lpsvUser to ensure that it isn't PT_ERROR, if so
         *  find the propval in rgpropvalsRecip ( the [in] recip prop val array ),
         *  if it exists and use that property.
         */

        for (iProp = 0; iProp < cValues; iProp++)
        {
            fUselpspvUser = TRUE;

            if ( PROP_TYPE( lpspvUser[iProp].ulPropTag ) == PT_ERROR )
            {
                lpPropVal = PpropFindProp( rgpropvalsRecip, cPropsRecip,
                         lpPropTagArray->aulPropTag[iProp] );

                if ( lpPropVal )
                {
                    sc = PropCopyMore(  lpNewRecip + iProp, lpPropVal,
                            lpABPLogon->lpAllocMore, lpNewRecip );

                    fUselpspvUser = FALSE;
                }
            }

            if ( fUselpspvUser )
            {
                sc = PropCopyMore(  lpNewRecip + iProp, lpspvUser + iProp,
                        lpABPLogon->lpAllocMore, lpNewRecip );
            }

            if (FAILED(sc))
            {
                if (sc == MAPI_E_NOT_ENOUGH_MEMORY)
                {
                    hResult = MakeResult(sc);
                    goto err;
                }

                /*
                 *   Otherwise we've run into something wierd in the prop value array
                 *   like PT_UNSPECIFIED, PT_NULL, or PT_OBJECT.  In which case continue
                 *   on.
                 */
            }
        }

        /* Done with lpspvUser */

        lpABPLogon->lpFreeBuff( lpspvUser );
        lpspvUser = NULL;

        /*
         *  Copy those properties that aren't already in lpNewRecip
         *  from rgpropvalsRecip.  Don't copy over the PT_ERROR prop vals
         */
        for ( iProp = 0; iProp < cPropsRecip; iProp++ )
        {

            if ( PpropFindProp( lpNewRecip, cValues, rgpropvalsRecip[iProp].ulPropTag )
                || PROP_TYPE( rgpropvalsRecip[iProp].ulPropTag ) == PT_ERROR )
                continue;

            sc = PropCopyMore(  lpNewRecip + cValues, rgpropvalsRecip + iProp,
                    lpABPLogon->lpAllocMore, lpNewRecip );
            if ( FAILED( sc ) )
            {
                if (sc == MAPI_E_NOT_ENOUGH_MEMORY)
                {

                    hResult = ResultFromScode( sc );
                    goto err;
                }

                /*
                 *  Otherwise we've run into something wierd in the prop value array
                 *  like PT_UNSPECIFIED, PT_NULL, or PT_OBJECT.  In which case continue
                 *  on.
                 */
            }

            cValues++;
        }

        /*
         *  Replace the AdrEntry in the AdrList with this new lpNewRecip.  And
         *  don't forget the cValues!
         */

        lpRecipList->aEntries[iRecip].rgPropVals = lpNewRecip;
        lpRecipList->aEntries[iRecip].cValues    = cValues;

        /* Finally, free up the old AdrEntry. */

        lpABPLogon->lpFreeBuff( rgpropvalsRecip );

    }
out:

    DebugTraceResult( ABPLOGON_PrepareRecips, hResult );
    return hResult;

err:

    lpABPLogon->lpFreeBuff( lpspvUser );
    goto out;
}


/*************************************************************************
 *  LpMuidFromLogon -
 *    Returns the particular ABPLOGON object's unique identifier.
 *
 */

LPMAPIUID
LpMuidFromLogon(LPABLOGON lpABLogon)
{
    LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon;

    AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "Bad logon object!\n");

    return (&(lpABPLogon->muidID));
}


/*************************************************************************
 *  HrLpszGetCurrentFileName -
 *    Returns a copy of the current .FAB file pointed to by this logon object.
 *
 */

HRESULT
HrLpszGetCurrentFileName(LPABLOGON lpABLogon, LPTSTR * lppszFileName)
{
    LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon;
    SCODE sc;
    HRESULT hResult = hrSuccess;

    AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "FAB: Bad logon object!\n");
    AssertSz(!IsBadWritePtr(lppszFileName, SIZEOF(LPTSTR)), "FAB: Bad dest string!\n");

    EnterCriticalSection(&lpABPLogon->cs);

    sc = lpABPLogon->lpAllocBuff( (lstrlen(lpABPLogon->lpszFileName)+1)*SIZEOF(TCHAR), lppszFileName);
    if (FAILED(sc))
    {
        hResult = ResultFromScode(sc);
        goto ret;
    }

    lstrcpy( *lppszFileName, lpABPLogon->lpszFileName);

ret:
    LeaveCriticalSection(&lpABPLogon->cs);

    DebugTraceResult(HrLpszGetCurrentFileName, hResult);
    return hResult;
}

/*
 *  HrReplaceCurrentFileName -
 *    Replaces the current file name associated with this logon object and tries
 *    to save it all away in the profile.
 */

HRESULT
HrReplaceCurrentFileName(LPABLOGON lpABLogon, LPTSTR lpszNewFile)
{
    LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon;
    HRESULT hResult = hrSuccess;
    LPPROFSECT lpProfSect = NULL;
    LPTSTR lpstrT;
    SCODE sc;
    SPropValue rgspv[1];
#ifdef UNICODE
    CHAR szAnsiFileName[ MAX_PATH ];
#endif

    AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "Bad logon object!\n");

    EnterCriticalSection(&lpABPLogon->cs);

    /*
     *  FAB file name has changed have to update profile and objects
     */
    if (lstrcmp(lpszNewFile, lpABPLogon->lpszFileName))
    {

        /*
         *  Open the private profile section...
         */
        hResult = lpABPLogon->lpMapiSup->lpVtbl->OpenProfileSection(
                          lpABPLogon->lpMapiSup,
                          NULL,
                          MAPI_MODIFY,
                          &lpProfSect);

        if (HR_FAILED(hResult))
        {
            /*
             *  Shouldn't get here, but in case I do, just...
             */
            goto ret;
        }

        /*
         *  Save the new name back into the profile
         */
        rgspv[0].ulPropTag  = PR_FAB_FILE_A;
#ifdef UNICODE
        szAnsiFileName[0] = 0;
        WideCharToMultiByte( CP_ACP, 0, lpszNewFile, -1, szAnsiFileName, ARRAYSIZE(szAnsiFileName), NULL, NULL );
        rgspv[0].Value.lpszA = szAnsiFileName;
#else
        rgspv[0].Value.LPSZ = lpszNewFile;
#endif

        /*
         *  Don't care if I can save it in the profile or not.
         *  Saving it's a nice to have, but absolutely required
         *  for operation of this particular provider.
         */
        (void) lpProfSect->lpVtbl->SetProps(
                       lpProfSect,
                       1,   // ansi
                       rgspv,
                       NULL);

        lpProfSect->lpVtbl->Release(lpProfSect);

        /*
         *  Allocate and copy this new one
         */

        sc = lpABPLogon->lpAllocBuff( (lstrlen(lpszNewFile)+1)*SIZEOF(TCHAR), &lpstrT);
        if (FAILED(sc))
        {
            hResult = ResultFromScode(sc);
            goto ret;
        }

        lstrcpy( lpstrT, lpszNewFile );

        /*
         *  Free up the old one...
         */
        lpABPLogon->lpFreeBuff(lpABPLogon->lpszFileName);

        /*
         *  Put in the new one.
         */
        lpABPLogon->lpszFileName = lpstrT;

        /*
         *  Update the hierarchy table
         */
        hResult = HrBuildRootHier((LPABLOGON)lpABPLogon, NULL);
    }

ret:
    LeaveCriticalSection(&lpABPLogon->cs);

    DebugTraceResult(HrReplaceCurrentFileName, hResult);
    return hResult;
}

/*
 *  GenerateContainerDN -
 *      Common code for generating the display name of the single
 *      container exposed from this logon object.
 */
#ifdef SAB      // from sample AB
void
GenerateContainerDN(LPABLOGON lpABLogon, LPTSTR lpszName)
{

    LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon;
    LPTSTR lpszFileName;
    int ich;

    AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "Bad logon object!\n");


    EnterCriticalSection(&lpABPLogon->cs);

    lpszFileName = lpABPLogon->lpszFileName;

    // get the filename without the path
    for (ich = lstrlen(lpszFileName) - 1; ich >= 0; ich--)
    {
        if (lpszFileName[ich] == TEXT('\\'))
            break;
    }

    // skip past the backslash
    ich++;

    wsprintf(lpszName, TEXT("FAB using %s"), lpszFileName + ich);

    LeaveCriticalSection(&lpABPLogon->cs);
}

#else   // Fax AB
void
GenerateContainerDN(HINSTANCE hInst, LPTSTR lpszName)
{

    LoadString (hInst, IDS_ADDRESS_BOOK_ROOT_CONT, lpszName, MAX_DISPLAY_NAME);

}
#endif

/*
 -  HrBuildRootHier
 -
 *
 *  Builds up the root hierarchy for the Microsoft At Work Fax Address Book.
 *
 *
 */
enum {  ivalPR_DISPLAY_NAME = 0,
        ivalPR_ENTRYID,
        ivalPR_DEPTH,
        ivalPR_OBJECT_TYPE,
        ivalPR_DISPLAY_TYPE,
        ivalPR_CONTAINER_FLAGS,
        ivalPR_INSTANCE_KEY,
        ivalPR_AB_PROVIDER_ID,
        cvalMax };

static const SizedSPropTagArray(cvalMax, tagaRootColSet) =
{
    cvalMax,
    {
        PR_DISPLAY_NAME_A,
        PR_ENTRYID,
        PR_DEPTH,
        PR_OBJECT_TYPE,
        PR_DISPLAY_TYPE,
        PR_CONTAINER_FLAGS,
        PR_INSTANCE_KEY,
        PR_AB_PROVIDER_ID
    }
};

HRESULT
HrBuildRootHier(LPABLOGON lpABLogon, LPMAPITABLE * lppMAPITable)
{
    HRESULT     hResult;
    SCODE       sc;
    SRow        sRow;
    SPropValue  rgsPropValue[cvalMax];
    ULONG       ulInstanceKey = 1;
    TCHAR       szBuf[MAX_PATH];
#ifdef UNICODE
    CHAR        szAnsiBuf[MAX_PATH];
#endif
    HINSTANCE   hInst;
    LPABPLOGON  lpABPLogon = (LPABPLOGON) lpABLogon;
    DIR_ENTRYID eidRoot =   {   {0, 0, 0, 0},
                                MUIDABMAWF,
                                MAWF_VERSION,
                                MAWF_DIRECTORY };


    EnterCriticalSection(&lpABPLogon->cs);

    hInst = lpABPLogon->hLibrary;

    /*
     *  See if we have a TaD yet
     */
    if (!lpABPLogon->lpTDatRoot)
    {
        /* Create a Table Data object */
        if ( sc = CreateTable((LPIID) &IID_IMAPITableData,
                        lpABPLogon->lpAllocBuff,
                        lpABPLogon->lpAllocMore,
                        lpABPLogon->lpFreeBuff,
                        lpABPLogon->lpMalloc,
                        0,
                        PR_ENTRYID,
                        (LPSPropTagArray) &tagaRootColSet,
                        &(lpABPLogon->lpTDatRoot))
            )
        {
            hResult = ResultFromScode(sc);
            goto out;
        }
    }
    /* Constants */

    sRow.cValues = cvalMax;
    sRow.lpProps = rgsPropValue;

    /* First, the Display Name stuff*/

    rgsPropValue[ivalPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_A;
#ifdef SAB
    GenerateContainerDN((LPABLOGON) lpABPLogon, szBuf);
#else
    GenerateContainerDN(hInst, szBuf);
#endif
#ifdef UNICODE
    szAnsiBuf[0] = 0;
    WideCharToMultiByte( CP_ACP, 0, szBuf, -1, szAnsiBuf, ARRAYSIZE(szAnsiBuf), NULL, NULL );
    rgsPropValue[ivalPR_DISPLAY_NAME].Value.lpszA = szAnsiBuf;
#else
    rgsPropValue[ivalPR_DISPLAY_NAME].Value.lpszA = szBuf;
#endif

    /*
     *  For each FAB logon object associated with it's init object,
     *  we have a unique MAPIUID.  It's the only thing that distinguishes
     *  one FAB entryid from another in the merged hierarchy table that
     *  MAPI generates.
     */

    rgsPropValue[ivalPR_ENTRYID].ulPropTag = PR_ENTRYID;
    eidRoot.muidID = lpABPLogon->muidID;
    rgsPropValue[ivalPR_ENTRYID].Value.bin.cb  = SIZEOF(DIR_ENTRYID);
    rgsPropValue[ivalPR_ENTRYID].Value.bin.lpb = (LPVOID) &eidRoot;

    rgsPropValue[ivalPR_DEPTH].ulPropTag = PR_DEPTH;
    rgsPropValue[ivalPR_DEPTH].Value.l   = 0;

    rgsPropValue[ivalPR_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE;
    rgsPropValue[ivalPR_OBJECT_TYPE].Value.l   = MAPI_ABCONT;

    rgsPropValue[ivalPR_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE;
    rgsPropValue[ivalPR_DISPLAY_TYPE].Value.l   = DT_NOT_SPECIFIC;

    rgsPropValue[ivalPR_CONTAINER_FLAGS].ulPropTag = PR_CONTAINER_FLAGS;
    rgsPropValue[ivalPR_CONTAINER_FLAGS].Value.l   = AB_RECIPIENTS | AB_UNMODIFIABLE;

    rgsPropValue[ivalPR_INSTANCE_KEY].ulPropTag     = PR_INSTANCE_KEY;
    rgsPropValue[ivalPR_INSTANCE_KEY].Value.bin.cb  = SIZEOF(ULONG);
    rgsPropValue[ivalPR_INSTANCE_KEY].Value.bin.lpb = (LPBYTE) &ulInstanceKey;

    rgsPropValue[ivalPR_AB_PROVIDER_ID].ulPropTag     = PR_AB_PROVIDER_ID;
    rgsPropValue[ivalPR_AB_PROVIDER_ID].Value.bin.cb  = SIZEOF(MAPIUID);
    rgsPropValue[ivalPR_AB_PROVIDER_ID].Value.bin.lpb = (LPBYTE) &muidABMAWF;

    hResult = lpABPLogon->lpTDatRoot->lpVtbl->HrModifyRow( lpABPLogon->lpTDatRoot, &sRow );

    if (HR_FAILED(hResult))
        goto out;


    /*
     *  Check to see if they want a view returned as well
     */
    if (lppMAPITable)
    {
        /* Get a view from the Table data object */
        hResult =
            lpABPLogon->lpTDatRoot->lpVtbl->HrGetView(
                    lpABPLogon->lpTDatRoot,
                    NULL,
                    NULL,
                    0,
                    lppMAPITable);
    }

out:

    LeaveCriticalSection(&lpABPLogon->cs);

    DebugTraceResult(HrBuildRootHier, hResult);
    return hResult;
}



/*
 *  Checks to see if the file passed in is still the actual file that
 *  should be browsed.
 */
BOOL
FEqualFABFiles( LPABLOGON lpABLogon,
                LPTSTR lpszFileName)
{
    LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon;
    BOOL fEqual;

    AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "Bad logon object!\n");

    EnterCriticalSection(&lpABPLogon->cs);

    fEqual = !lstrcmp( lpszFileName, lpABPLogon->lpszFileName );

    LeaveCriticalSection(&lpABPLogon->cs);

    return fEqual;
}