/*++

(c) 1998 Seagate Software, Inc.  All rights reserved.

Module Name:

    fsaftclt.cpp

Abstract:

    This class represents a user who the filter has detected accessing a file with placeholder information.

Author:

    Chuck Bardeen   [cbardeen]   12-Feb-1997

Revision History:

--*/

#include "stdafx.h"
extern "C" {
#include <ntseapi.h>
#include <wchar.h>
#include <lmaccess.h>
#include <lmserver.h>
#include <lmshare.h>
#include <lmapibuf.h>
#include <lmerr.h>

// #define MAC_SUPPORT  // NOTE: You must define MAC_SUPPORT in fsafltr.cpp to enable all the code

#ifdef MAC_SUPPORT
#include <macfile.h>
#endif  
}


#define WSB_TRACE_IS        WSB_TRACE_BIT_FSA

#include "wsb.h"
#include "fsa.h"
#include "fsaftclt.h"

static USHORT iCountFtclt = 0;  // Count of existing objects

//
// We need to dynamically load the DLL for MAC support because it is not there if
// the MAC service is not installed.
//
#ifdef MAC_SUPPORT
HANDLE      FsaDllSfm = 0;
BOOL        FsaMacSupportInstalled = FALSE;

extern "C" {
DWORD   (*pAfpAdminConnect) (LPWSTR lpwsServerName, PAFP_SERVER_HANDLE phAfpServer);
VOID    (*pAfpAdminDisconnect) (AFP_SERVER_HANDLE hAfpServer);
VOID    (*pAfpAdminBufferFree) (PVOID pBuffer);
DWORD   (*pAfpAdminSessionEnum) (AFP_SERVER_HANDLE hAfpServer, LPBYTE *lpbBuffer,
            DWORD dwPrefMaxLen, LPDWORD lpdwEntriesRead, LPDWORD lpdwTotalEntries,
            LPDWORD lpdwResumeHandle);
}
#endif  

DWORD FsaIdentifyThread(void *pNotifyInterface);



DWORD FsaIdentifyThread(
    void* pVoid
    )

/*++
    Entry point of the thread that performs identify operation with remote clients.

--*/
{
HRESULT     hr;

    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    hr = ((CFsaFilterClient*) pVoid)->IdentifyThread();
    CoUninitialize();
    return(hr);
}



static
HRESULT
GetNotifyClientInterface(
    IN  OLECHAR * machineName,
    OUT IFsaRecallNotifyClient ** ppClient
    )
{
    HRESULT hr = S_OK;

    try {

        //
        // Make sure parameters OK and OUTs initially cleared
        //

        WsbAffirmPointer ( ppClient );
        *ppClient = 0;

        //
        // If connecting local, things work better to use NULL
        // for the computer name
        //

        if ( machineName ) {

            CWsbStringPtr localMachine;
            WsbAffirmHr( WsbGetComputerName( localMachine ) );

            if( _wcsicmp( localMachine, machineName ) == 0 ) {

                machineName = 0;

            }

        }

        //
        // Set server info
        //
        COSERVERINFO        csi;
        COAUTHINFO          cai;
        memset ( &csi, 0, sizeof ( csi ) );
        memset ( &cai, 0, sizeof ( cai ) );

        // Set machine name
        csi.pwszName  = machineName;

        // Create a proxy with security settings of no authentication (note that RsNotify is running with this security)
        cai.dwAuthnSvc = RPC_C_AUTHN_WINNT;
        cai.dwAuthzSvc = RPC_C_AUTHZ_DEFAULT;
        cai.pwszServerPrincName = NULL;
        cai.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
        cai.pAuthIdentityData = NULL;
        cai.dwCapabilities = EOAC_NONE;

        cai.dwAuthnLevel = RPC_C_AUTHN_LEVEL_NONE;

        csi.pAuthInfo = &cai;

        //
        // We want IFsaRecallNotifyClient back
        //

        MULTI_QI            mqi;
        memset ( &mqi, 0, sizeof ( mqi ) );
        mqi.pIID = &IID_IFsaRecallNotifyClient;

        //
        // Make the connection...
        //

        WsbAffirmHr ( CoCreateInstanceEx ( 
            CLSID_CFsaRecallNotifyClient, 0, 
	        CLSCTX_NO_FAILURE_LOG | ( machineName ? CLSCTX_REMOTE_SERVER : CLSCTX_LOCAL_SERVER ), 
            &csi, 1, &mqi ) );
        WsbAffirmHr ( mqi.hr );

        //
        // We need to make sure we clean up correctly if any interface
        // post-processing fails, so assign over to a smart pointer for
        // the time being
        //

        CComPtr<IFsaRecallNotifyClient> pClientTemp = (IFsaRecallNotifyClient*)mqi.pItf;
        mqi.pItf->Release ( );

        //
        // Finally, we need to set the security on the procy to allow the
        // anonymous connection. Values should be the same as above (COAUTHINFO)
        // We need to make sure this is a remote machine first. Otherwise, we
        // get an error of E_INVALIDARG.
        //
        if( machineName ) {

            CComPtr<IClientSecurity> pSecurity;
            WsbAffirmHr( pClientTemp->QueryInterface( IID_IClientSecurity, (void**)&pSecurity ) );

            WsbAffirmHr( pSecurity->SetBlanket ( pClientTemp, RPC_C_AUTHN_NONE, RPC_C_AUTHZ_NONE, 0, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IDENTIFY, 0, 0 ) );

        }

        //
        // Finally, assign over and AddRef the return.
        //

        *ppClient = pClientTemp;
        (*ppClient)->AddRef ( );

    } WsbCatch ( hr );

    return ( hr );
}



HRESULT
CFsaFilterClient::CheckRecallLimit(
    IN DWORD   minRecallInterval,
    IN DWORD   maxRecalls,
    IN BOOLEAN exemptAdmin
    )

/*++

Implements:

  IWsbCollectable::CheckRecallLimit().

--*/
{
    HRESULT                     hr = S_OK;
    FILETIME                    now, last;
    LARGE_INTEGER               tNow, tLast;
    ULONG                       rCount;


    WsbTraceIn(OLESTR("CFsaFilterClient::CheckRecallLimit"), OLESTR(""));
    
    try {
        //
        // Now check for runaway recall limits if the user is not
        // an administrator
        //
        
        if ((!m_isAdmin) || (!exemptAdmin)) {
            //
            // See if the time since the end of the last recall is 
            // less than m_minRecallInterval (in seconds) and if so, 
            // increment the count.
            // If not, then reset the count (if we were not 
            // already triggered).
            // If the count is equal to the max then set the trigger.
            //
            WsbTrace(OLESTR("CHsmFilter::IoctlThread: Not an administrator or admin is not exempt.\n"));
            GetSystemTimeAsFileTime(&now);
            tNow.LowPart = now.dwLowDateTime;
            tNow.HighPart = now.dwHighDateTime;
    
            GetLastRecallTime(&last);
    
            tLast.LowPart = last.dwLowDateTime;
            tLast.HighPart = last.dwHighDateTime;
            //
            //  Get the time (in 100 nano-second units)
            //  from the end of the last recall until now.
            //
            tNow.QuadPart -= tLast.QuadPart;
            //
            // Convert to seconds and check against the interval time
            //
            tNow.QuadPart /= (LONGLONG) 10000000;
            if (tNow.QuadPart < (LONGLONG) minRecallInterval) {
                //
                // This one counts - increment the count
                // and check for a trigger.
                //
                GetRecallCount(&rCount);
                rCount++;
                SetRecallCount(rCount);
    
                WsbTrace(OLESTR("CHsmFilterClient::CheckRecallLimit: Recall count bumped to %ls.\n"),
                        WsbLongAsString(rCount));
    
                if (rCount >= maxRecalls) {
                    // 
                    // Hit the runaway recall limit.  Set the 
                    // limit flag.
                    //
                    WsbTrace(OLESTR("CHsmFilter::IoctlThread: Hit the runaway recall limit!!!.\n"));
                    SetHitRecallLimit(TRUE);
                }
            } else {
                //
                // Reset the count if they are not already triggered.
                // If they are triggered then reset the trigger and
                // limit if it has been a respectable time.
                // TBD - What is a respectable time??
                //
                if (HitRecallLimit() != S_FALSE) {
                    if (tNow.QuadPart > (LONGLONG) minRecallInterval * 100) {
                        //
                        // A respectable time has passed - reset the trigger and count.
                        //
                        WsbTrace(OLESTR("CHsmFilterClient::CheckRecallLimit: Resetting recall limit trigger and count.\n"));
                        SetHitRecallLimit(FALSE);
                        SetRecallCount(0);
                        m_loggedLimitError = FALSE;
                    }
                } else {
                    //
                    // This one did not count and they were not already triggered.
                    // Reset the count to zero.
                    //
                    WsbTrace(OLESTR("CHsmFilterClient::CheckRecallLimit: Resetting recall count.\n"));
                    SetRecallCount(0);
                }
            }
            //
            // Fail if the limit is hit.
            //
            WsbAffirm(HitRecallLimit() == S_FALSE, FSA_E_HIT_RECALL_LIMIT);
        }

    } WsbCatch(hr);

    //  NOTE - IF RUNAWAY RECALL BEHAVIOR CHANGES TO TRUNCATE ON CLOSE, CHANGE
    //  FSA_MESSAGE_HIT_RECALL_LIMIT_ACCESSDENIED TO FSA_MESSAGE_HIT_RECALL_LIMIT_TRUNCATEONCLOSE.

    if ( (hr == FSA_E_HIT_RECALL_LIMIT) && (!m_loggedLimitError)) {
        WsbLogEvent(FSA_MESSAGE_HIT_RECALL_LIMIT_ACCESSDENIED, 0, NULL, (WCHAR *) m_userName, NULL);
        m_loggedLimitError = TRUE;
    }

    WsbTraceOut(OLESTR("CHsmFilterClient::CheckRecallLimit"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaFilterClient::CompareBy(
    FSA_FILTERCLIENT_COMPARE by
    )

/*++

Implements:

  IFsaFilterClient::CompareBy().

--*/
{
    HRESULT                 hr = S_OK;

    m_compareBy = by;
    m_isDirty = TRUE;

    return(hr);
}


HRESULT
CFsaFilterClient::CompareTo(
    IN IUnknown* pUnknown,
    OUT SHORT* pResult
    )

/*++

Implements:

  IWsbCollectable::CompareTo().

--*/
{
    HRESULT                     hr = S_OK;
    CComPtr<IFsaFilterClient>   pClient;

    WsbTraceIn(OLESTR("CFsaFilterClient::CompareTo"), OLESTR(""));
    
    try {

        // Did they give us a valid item to compare to?
        WsbAssert(0 != pUnknown, E_POINTER);

        // We need the IWsbBool interface to get the value of the object.
        WsbAffirmHr(pUnknown->QueryInterface(IID_IFsaFilterClient, (void**) &pClient));

        // Compare the rules.
        hr = CompareToIClient(pClient, pResult);

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CHsmFilterClient::CompareTo"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult));

    return(hr);
}


HRESULT
CFsaFilterClient::CompareToAuthenticationId(
    IN LONG luidHigh,
    IN ULONG luidLow,
    OUT SHORT* pResult
    )

/*++

Implements:

  IFsaFilterClient::CompareToAuthenticationId().

--*/
{
    HRESULT     hr = S_OK;
    SHORT       aResult;

    WsbTraceIn(OLESTR("CFsaFilterClient::CompareToAuthenticationId"), OLESTR(""));

    try {

        if (m_luidHigh > luidHigh) {
            aResult = 1;
        } else if (m_luidHigh < luidHigh) {
            aResult = -1;
        } else if (m_luidLow > luidLow) {
            aResult = 1;
        } else if (m_luidLow < luidLow) {
            aResult = -1;
        } else {
            aResult = 0;
        }

        if (0 != aResult) {
            hr = S_FALSE;
        }

        if (0 != pResult) {
            *pResult = aResult;
        }


    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaFilterClient::CompareToAuthenticationId"), OLESTR("hr = <%ls>, result = <%d>"), WsbHrAsString(hr), aResult);

    return(hr);
}


HRESULT
CFsaFilterClient::CompareToIClient(
    IN IFsaFilterClient* pClient,
    OUT SHORT* pResult
    )

/*++

Implements:

  IFsaFilterClient::CompareToIClient().

--*/
{
    HRESULT         hr = S_OK;
    CWsbStringPtr   name;
    LONG            luidHigh;
    ULONG           luidLow;

    WsbTraceIn(OLESTR("CFsaFilterClient::CompareToIClient"), OLESTR(""));

    try {

        // Did they give us a valid item to compare to?
        WsbAssert(0 != pClient, E_POINTER);

        switch (m_compareBy) {
        case FSA_FILTERCLIENT_COMPARE_ID:
            WsbAffirmHr(pClient->GetAuthenticationId(&luidHigh, &luidLow));
            hr = CompareToAuthenticationId(luidHigh, luidLow, pResult);
            break;
        case FSA_FILTERCLIENT_COMPARE_MACHINE:
            WsbAffirmHr(pClient->GetMachineName(&name, 0));
            hr = CompareToMachineName(name, pResult);
            break;
        default:
            WsbAssert(FALSE, E_UNEXPECTED);
        }

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaFilterClient::CompareToIClient"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult));

    return(hr);
}


HRESULT
CFsaFilterClient::CompareToMachineName(
    IN OLECHAR* name,
    OUT SHORT* pResult
    )

/*++

Implements:

  IFsaFilterClient::CompareToMachineName().

--*/
{
    HRESULT     hr = S_OK;
    SHORT       aResult;

    WsbTraceIn(OLESTR("CFsaFilterClient::CompareToMachineName"), OLESTR(""));

    try {

        aResult = (SHORT)wcscmp(name, m_machineName); // TBD - Case sensitive or not?

        if (0 != aResult) {
            hr = S_FALSE;
        }
        
        if (0 != pResult) {
            *pResult = aResult;
        }

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaFilterClient::CompareToMachineName"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaFilterClient::FinalConstruct(
    void
    )

/*++

Implements:

  CComObjectRoot::FinalConstruct().

--*/
{
    HRESULT     hr = S_OK;
    
    WsbTraceIn(OLESTR("CFsaFilterClient::FinalConstruct"),OLESTR(""));
    try {

        WsbAffirmHr(CWsbCollectable::FinalConstruct());

        m_compareBy = FSA_FILTERCLIENT_COMPARE_ID;
        m_luidHigh = 0;
        m_luidLow = 0;
        m_hasRecallDisabled = FALSE;
        m_hitRecallLimit = FALSE;
        m_lastRecallTime.dwLowDateTime = 0; 
        m_lastRecallTime.dwHighDateTime = 0;    
        m_identified = FALSE;
        m_tokenSource = L"";
        m_msgCounter = 1;
        m_identifyThread = NULL;
        m_isAdmin = FALSE;
        m_loggedLimitError = FALSE;
    
    } WsbCatch(hr);

    iCountFtclt++;
    WsbTraceOut(OLESTR("CFsaFilterClient::FinalConstruct"),OLESTR("Count is <%d>"), iCountFtclt);

    return(hr);
}
void
CFsaFilterClient::FinalRelease(
    void
    )

/*++

Implements:

  CComObjectRoot::FinalRelease().

--*/
{
    WsbTraceIn(OLESTR("CFsaFilterClient::FinalRelease"),OLESTR(""));
   
    CWsbCollectable::FinalRelease();

    iCountFtclt--;
    WsbTraceOut(OLESTR("CFsaFilterClient::FinalRelease"),OLESTR("Count is <%d>"), iCountFtclt);
}


HRESULT
CFsaFilterClient::GetClassID(
    OUT CLSID* pClsid
    )

/*++

Implements:

  IPersist::GetClassID().

--*/
{
    HRESULT     hr = S_OK;

    WsbTraceIn(OLESTR("CFsaFilterClient::GetClassID"), OLESTR(""));

    try {

        WsbAssert(0 != pClsid, E_POINTER);
        *pClsid = CLSID_CFsaFilterClientNTFS;

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaFilterClient::GetClassID"), OLESTR("hr = <%ls>, CLSID = <%ls>"), WsbHrAsString(hr), WsbGuidAsString(*pClsid));

    return(hr);
}


HRESULT
CFsaFilterClient::GetAuthenticationId(
    OUT LONG* pLuidHigh,
    OUT ULONG* pLuidLow
    )

/*++

Implements:

  IFsaFilterClient::GetAuthenticationId().

--*/
{
    HRESULT         hr = S_OK;

    try {

        WsbAssert(0 != pLuidHigh, E_POINTER);
        WsbAssert(0 != pLuidLow, E_POINTER);

        *pLuidHigh = m_luidHigh;
        *pLuidLow = m_luidLow;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaFilterClient::GetDomainName(
    OUT OLECHAR** pName,
    IN ULONG bufferSize
    )

/*++

Implements:

  IFsaFilterClient::GetDomainName().

--*/
{
    HRESULT         hr = S_OK;

    try {

        WsbAssert(0 != pName, E_POINTER);
        WsbAffirmHr(m_domainName.CopyTo(pName, bufferSize));

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaFilterClient::GetIsAdmin(
    OUT BOOLEAN *pIsAdmin
    )

/*++

Implements:

  IPersist::GetIsAdmin().

--*/
{
    HRESULT     hr = S_OK;

    WsbTraceIn(OLESTR("CFsaFilterClient::GetIsAdmin"), OLESTR(""));

    try {

        WsbAssert(0 != pIsAdmin, E_POINTER);
        *pIsAdmin = m_isAdmin;

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaFilterClient::GetIsAdmin"), OLESTR("hr = <%ls>, isAdmin = <%u>"), WsbHrAsString(hr), *pIsAdmin);

    return(hr);
}


HRESULT
CFsaFilterClient::GetLastRecallTime(
    OUT FILETIME* pTime
    )

/*++

Implements:

  IFsaFilterClient::GetLastRecallTime().

--*/
{
    HRESULT         hr = S_OK;

    try {

        WsbAssert(0 != pTime, E_POINTER); 
        *pTime = m_lastRecallTime;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaFilterClient::GetMachineName(
    OUT OLECHAR** pName,
    IN ULONG bufferSize
    )

/*++

Implements:

  IFsaFilterClient::GetMachineName().

--*/
{
    HRESULT         hr = S_OK;

    try {

        WsbAssert(0 != pName, E_POINTER);
        WsbAffirmHr(m_machineName.CopyTo(pName, bufferSize));

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaFilterClient::GetRecallCount(
    OUT ULONG* pCount
    )

/*++

Implements:

  IFsaFilterClient::GetRecallCount().

--*/
{
    HRESULT         hr = S_OK;

    try {

        WsbAssert(0 != pCount, E_POINTER); 
        *pCount = m_recallCount;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaFilterClient::GetUserName(
    OUT OLECHAR** pName,
    IN ULONG bufferSize
    )

/*++

Implements:

  IFsaFilterClient::GetUserName().

--*/
{
    HRESULT         hr = S_OK;

    try {

        WsbAssert(0 != pName, E_POINTER);
        WsbAffirmHr(m_userName.CopyTo(pName, bufferSize));

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaFilterClient::GetSizeMax(
    OUT ULARGE_INTEGER* pSize
    )

/*++

Implements:

  IPersistStream::GetSizeMax().

--*/
{
    HRESULT                 hr = S_OK;


    WsbTraceIn(OLESTR("CFsaFilterClient::GetSizeMax"), OLESTR(""));

    try {

        WsbAssert(0 != pSize, E_POINTER);
        pSize->QuadPart = 0;

        // WE don't need to persist these.
        hr = E_NOTIMPL;

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaFilterClient::GetSizeMax"), OLESTR("hr = <%ls>, Size = <%ls>"), WsbHrAsString(hr), WsbPtrToUliAsString(pSize));

    return(hr);
}


HRESULT
CFsaFilterClient::HasRecallDisabled(
    void
    )

/*++

Implements:

  IFsaFilterClient::HasRecallDisabled().

--*/
{
    HRESULT                 hr = S_OK;

    WsbTraceIn(OLESTR("CFsaFilterClient::HasRecallDisabled"), OLESTR(""));
    
    if (!m_hasRecallDisabled) {
        hr = S_FALSE;
    }

    WsbTraceOut(OLESTR("CHsmFilterClient::HasRecallDisabled"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaFilterClient::HitRecallLimit(
    void
    )

/*++

Implements:

  IFsaFilterClient::HitRecallLimit().

--*/
{
    HRESULT                 hr = S_OK;

    WsbTraceIn(OLESTR("CFsaFilterClient::HitRecallLimit"), OLESTR(""));
    
    if (!m_hitRecallLimit) {
        hr = S_FALSE;
    }

    WsbTraceOut(OLESTR("CHsmFilterClient::HitRecallLimit"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}



HRESULT
CFsaFilterClient::IdentifyThread(
    void
    )

/*++

Implements:

  CFsaFilterClient::IdentifyThread().

--*/
{
#define WSB_BUFF_SIZE           1024

    HRESULT             hr = S_OK;
    BOOL                done, guestUser, noUser;
    DWORD               res, totalEnt, numEnt;
    UCHAR               *buff = NULL;
    NET_API_STATUS      status;
    SESSION_INFO_1      *sess;
    CWsbStringPtr       pipePath;
    ULONG               holdOff = 0;
#ifdef MAC_SUPPORT
    LPBYTE              macBuff = NULL;
    PAFP_SESSION_INFO   macInfo;
    AFP_SERVER_HANDLE   macHandle = 0;
    DWORD               macResume = 0;
    DWORD               macTotalEntries, macTotalRead;
    DWORD               result;
#endif


    WsbTraceIn(OLESTR("CFsaFilterClient::IdentifyThread"), OLESTR(""));

    try {
        WsbTrace(OLESTR("CFsaFilterClient::IdentifyThread Flag: %x  Client ID: %x:%x Source: %ls\n"), 
            m_identified, m_luidHigh, m_luidLow, (WCHAR *) m_tokenSource);

        //
        // If already identified then we bail out here.
        //
        WsbAffirm(m_identified == FALSE, S_OK);

        
        done = FALSE;
        res = 0;
        
        noUser = FALSE;
        if (_wcsicmp(m_userName, L"GUEST") == 0) {
            /* It is the guest user - find all sessions and
                send to ones marked guest */
            guestUser = TRUE;
        } else {
            guestUser = FALSE;
            if (wcslen(m_userName) == 0) {
                noUser = TRUE;
            }
        }

        CComPtr<IFsaRecallNotifyClient> pRecallClient;

        WsbGetComputerName( pipePath );

        WsbAffirmHr(pipePath.Prepend("\\\\"));
        WsbAffirmHr(pipePath.Append("\\pipe\\"));
        WsbAffirmHr(pipePath.Append(WSB_PIPE_NAME));

        while ( done == FALSE ) {

            if ( (guestUser == FALSE) && (noUser == FALSE) ) {

                // If NetSessionEnum fails, try calling again for all users
                status = NetSessionEnum(NULL, NULL, m_userName, 1, &buff,
                                WSB_BUFF_SIZE, &numEnt, &totalEnt, &res);

                if (status != 0) {
                    status = NetSessionEnum(NULL, NULL, NULL, 1, &buff,
                                    WSB_BUFF_SIZE, &numEnt, &totalEnt, &res);
                }
            } else {
                status = NetSessionEnum( NULL, NULL, NULL, 1, &buff,
                    WSB_BUFF_SIZE, &numEnt, &totalEnt, &res );
            }

            if ((status == NERR_Success) || (status == ERROR_MORE_DATA)) {

                WsbTrace(OLESTR("CHsmFilterClient::IdentifyThread: NetSessionEnum output: Total entries=%ls , Read entries=%ls \n"),
                        WsbLongAsString(totalEnt), WsbLongAsString(numEnt));

                if (status != ERROR_MORE_DATA) {
                    done = TRUE;
                }

                sess = (SESSION_INFO_1  *) buff;

                while ( numEnt != 0 ) {
                    //
                    // If the request was made from the user GUEST then 
                    // we enumerate all sessions and send the           
                    // identification request to all the machines with  
                    // sessions marked as GUEST.  This is because the   
                    // session may have some other user name but the    
                    // request could still have GUEST access.           
                    //
                    if (((guestUser) && (sess->sesi1_user_flags & SESS_GUEST)) ||
                         (!guestUser)) {

                        //
                        // Send the identify request message 
                        //

                        WsbTrace(OLESTR("CFsaFilterClient::IdentifyThread - Sending identify request to %ls (local machine = %ls)\n"),
                                sess->sesi1_cname, (WCHAR *) pipePath);

                        hr = GetNotifyClientInterface ( sess->sesi1_cname, &pRecallClient );
                        if ( SUCCEEDED ( hr ) ) {

                            hr = pRecallClient->IdentifyWithServer( pipePath );
                            if (hr != S_OK) {
                                WsbTrace(OLESTR("CFsaFilterClient::IdentifyThread - error Identifing (%ls)\n"),
                                    WsbHrAsString(hr));
                            }
                        } else {
                            WsbTrace(OLESTR("CFsaFilterClient::IdentifyThread - error getting notify client interface hr = %ls (%x)\n"),
                                WsbHrAsString( hr ), hr);
                        }
                        hr = S_OK;
                        pRecallClient.Release ( );
                    }

                    sess++;
                    numEnt--;
                }

                NetApiBufferFree(buff);
                buff = NULL;
            } else {
                done = TRUE;
            }
        }
    
#ifdef MAC_SUPPORT
        //
        // Done with LAN manager scan, now do a MAC scan.
        //
        if ( (FsaMacSupportInstalled) && ((pAfpAdminConnect)(NULL, &macHandle) == NO_ERROR) ) {
            //
            // We have connected to the MAC service - do a session enumeration
            //
            macResume = 0;
            done = FALSE;   
            while (done == FALSE) {
                result = (pAfpAdminSessionEnum)(macHandle, &macBuff, -1,
                        &macTotalRead, &macTotalEntries, &macResume);

                if ((result == NO_ERROR) || (result == ERROR_MORE_DATA)) {
                        //
                        // Read some entries - send the message to each one 
                        //
                        if (macTotalRead == macTotalEntries) {
                            done = TRUE;
                        }

                        macInfo = (PAFP_SESSION_INFO) macBuff;
                        while ( macTotalRead != 0 ) {
                            //
                            // Send to each matching user
                            //
                            if ( ( NULL != macInfo->afpsess_ws_name ) &&
                                 ( _wcsicmp(m_userName, macInfo->afpsess_username ) == 0 ) ) {

                                WsbTrace(OLESTR("CHsmFilterClient::IdentifyThread: Send Identify to MAC %ls.\n"),
                                    macInfo->afpsess_ws_name);

                                //
                                // Send the identify request message 
                                //
            
                                hr = GetNotifyClientInterface ( sess->sesi1_cname, &pRecallClient );
                                if ( SUCCEEDED ( hr ) ) {
                                    pRecallClient->IdentifyWithServer ( pipePath );
                                }

                                hr = S_OK;
                                pRecallClient.Release ( );
                            }
                        macInfo++;
                        macTotalRead--;
                        }

                        (pAfpAdminBufferFree)(macBuff);
                        macBuff = NULL;
                } else {
                    done = TRUE;
                }
            (pAfpAdminDisconnect)(macHandle);
            macHandle = 0;
            }
        }
#endif
        
    } WsbCatch(hr);

    if (buff != NULL) {
        NetApiBufferFree(buff);
    }

#ifdef MAC_SUPPORT
    
    if (FsaMacSupportInstalled) {
        if (macBuff != NULL) {
            (pAfpAdminBufferFree)(macBuff);
        }
        if (macHandle != 0) {
            (pAfpAdminDisconnect)(macHandle);
        }
    }
#endif

    CloseHandle(m_identifyThread);
    m_identifyThread = NULL;

    WsbTraceOut(OLESTR("CFsaFilterClient::IdentifyThread"), OLESTR("hr = %ls"), WsbHrAsString(hr));
    return(hr);
}



HRESULT
CFsaFilterClient::Load(
    IN IStream* pStream
    )

/*++

Implements:

  IPersistStream::Load().

--*/
{
    HRESULT                     hr = S_OK;

    WsbTraceIn(OLESTR("CFsaFilterClient::Load"), OLESTR(""));

    try {
        WsbAssert(0 != pStream, E_POINTER);
        
        // No persistence.
        hr = E_NOTIMPL;

    } WsbCatch(hr);                                        

    WsbTraceOut(OLESTR("CFsaFilterClient::Load"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaFilterClient::Save(
    IN IStream* pStream,
    IN BOOL clearDirty
    )

/*++

Implements:

  IPersistStream::Save().

--*/
{
    HRESULT                 hr = S_OK;
    CComPtr<IPersistStream> pPersistStream;

    WsbTraceIn(OLESTR("CFsaFilterClient::Save"), OLESTR("clearDirty = <%ls>"), WsbBoolAsString(clearDirty));
    
    try {
        WsbAssert(0 != pStream, E_POINTER);
        
        // No persistence.
        hr = E_NOTIMPL;

        // If we got it saved and we were asked to clear the dirty bit, then
        // do so now.
        if (clearDirty) {
            m_isDirty = FALSE;
        }

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaFilterClient::Save"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaFilterClient::SendRecallInfo(
    IFsaFilterRecall *pRecall,
    BOOL             starting,
    HRESULT          rHr
    )

/*++

Implements:

  CFsaFilterClient::SendRecallInfo

--*/
{
    HRESULT hr = E_FAIL;

    if( ! m_identified && ( m_identifyThread != NULL ) ) {

        //
        // Wait for up to 10 seconds for identify thread to complete if
        // Client not yet identified
        //
        WaitForMultipleObjects( 1, &m_identifyThread, FALSE, 10000 );
    }

    //
    // Let the client know that the recall is starting or is finished
    //

    if ( m_identified ) {
    
        try {
            WsbTrace(OLESTR("CFsaFilterClient::SendRecallInfo - Client (%ls) is being notified of recall status (starting = %u hr = %x).\n"),
                        (WCHAR *) m_machineName, starting, rHr);
    
            //
            // Create intermediate server object which will be the client's
            // connection back to the service. This object acts as a middle
            // man to overcome the admin-only access into the FSA service.
            //
            CComPtr<IFsaRecallNotifyServer> pRecallServer;
            WsbAffirmHr(CoCreateInstance(CLSID_CFsaRecallNotifyServer, 0, CLSCTX_NO_FAILURE_LOG | CLSCTX_ALL, IID_IFsaRecallNotifyServer, (void**)&pRecallServer));
            WsbAffirmHr(pRecallServer->Init(pRecall));

            CComPtr<IFsaRecallNotifyClient> pRecallClient;
            hr = GetNotifyClientInterface ( m_machineName, &pRecallClient );
            if ( SUCCEEDED( hr ) ) {
                if (starting) {
                    hr = pRecallClient->OnRecallStarted ( pRecallServer );
                } else {
                    hr = pRecallClient->OnRecallFinished ( pRecallServer, rHr );
                }
                if (hr != S_OK) {
                    WsbTrace(OLESTR("CFsaFilterClient::SendRecallInfo - Got the interface but failed in OnRecall... (%ls)\n"), 
                        WsbHrAsString(hr));
                }
            } else {
                WsbTrace(OLESTR("CFsaFilterClient::SendRecallInfo - error getting notify client interface hr = %ls (%x)\n"),
                    WsbHrAsString( hr ), hr);
            }
        } WsbCatchAndDo(hr, 
            WsbTrace(OLESTR("CFsaFilterClient::SendRecallInfo - hr = %ls\n"),
                WsbHrAsString(hr));
            );
    }

    return(hr);

}



HRESULT
CFsaFilterClient::SetAuthenticationId(
    IN LONG luidHigh,
    IN ULONG luidLow
    )

/*++

Implements:

  IFsaFilterClient::SetAuthenticationId().

--*/
{
    m_luidHigh = luidHigh;
    m_luidLow = luidLow;

    return(S_OK);
}


HRESULT
CFsaFilterClient::SetDomainName(
    IN OLECHAR* name
    )

/*++

Implements:

  IFsaFilterClient::SetDomainName().

--*/
{
    HRESULT         hr = S_OK;

    try {

        m_domainName = name;
        WsbAssert(m_domainName != 0, E_UNEXPECTED);

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaFilterClient::SetIsAdmin(
    IN BOOLEAN isAdmin
    )

/*++

Implements:

  IFsaFilterClient::SetIsAdmin().

--*/
{
    m_isAdmin = isAdmin;

    return(S_OK);
}


HRESULT
CFsaFilterClient::SetLastRecallTime(
    IN FILETIME time
    )

/*++

Implements:

  IFsaFilterClient::SetLastRecallTime().

--*/
{
    m_lastRecallTime = time;

    return(S_OK);
}


HRESULT
CFsaFilterClient::SetMachineName(
    IN OLECHAR* name
    )

/*++

Implements:

  IFsaFilterClient::SetMachineName().

--*/
{
    HRESULT         hr = S_OK;

    try {
        WsbAssert(name != 0, E_UNEXPECTED);

        m_machineName = name;
        m_identified = TRUE;

        WsbTrace(OLESTR("CFsaFilterClient::SetMachineName Flag: %x  Client ID: %x:%x Source: %ls == %ls\n"), 
            m_identified, m_luidLow, m_luidHigh, (WCHAR *) m_tokenSource, (WCHAR *) m_machineName);


    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaFilterClient::SetRecallCount(
    IN ULONG count
    )

/*++

Implements:

  IFsaFilterClient::SetRecallCount().

--*/
{
    m_recallCount = count;

    return(S_OK);
}


HRESULT
CFsaFilterClient::SetUserName(
    IN OLECHAR* name
    )

/*++

Implements:

  IFsaFilterClient::SetUserName().

--*/
{
    HRESULT         hr = S_OK;

    try {

        m_userName = _wcsupr(name);
        WsbAssert(m_userName != 0, E_UNEXPECTED);

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaFilterClient::SetHasRecallDisabled(
    IN BOOL     hasBeen
    )

/*++

Implements:

  IFsaFilterClient::SetHasRecallDisabled().

--*/
{
    m_hasRecallDisabled = hasBeen;

    return(S_OK);
}


HRESULT
CFsaFilterClient::SetHitRecallLimit(
    IN BOOL     hasBeen
    )

/*++

Implements:

  IFsaFilterClient::SetHitRecallLimit().

--*/
{
    m_hitRecallLimit = hasBeen;

    return(S_OK);
}




HRESULT
CFsaFilterClient::SetTokenSource(
    IN CHAR     *source
    )

/*++

Implements:

  IFsaFilterClient::SetTokenSource()

--*/
{
OLECHAR tSource[TOKEN_SOURCE_LENGTH + 1];


    mbstowcs((WCHAR *) tSource, source, TOKEN_SOURCE_LENGTH);   
    m_tokenSource = tSource;
    return(S_OK);
}


HRESULT
CFsaFilterClient::StartIdentify(
    void
    )

/*++

Implements:

  CFsaFilterClient::StartIdentify().

--*/
{
#define WSB_BUFF_SIZE           1024

    HRESULT             hr = S_OK;
    DWORD                   tid;


    WsbTraceIn(OLESTR("CFsaFilterClient::StartIdentify"), OLESTR(""));

    try {
        WsbTrace(OLESTR("CFsaFilterClient::StartIdentify Flag: %x  Client ID: %x:%x Source: %ls\n"), 
            m_identified, m_luidHigh, m_luidLow, (WCHAR *) m_tokenSource);

        //
        // If already identified then we bail out here.
        //
        WsbAffirm(m_identified == FALSE, S_OK);
        //
        // If the request is from User32 then it is local 
        //

        if (_wcsicmp(m_tokenSource, L"User32") == 0) {

            //
            // Identified as the local machine.
            // Set the name and bail out with S_OK
            //
            WsbGetComputerName( m_machineName );
            m_identified = TRUE;

            WsbTrace(OLESTR("CHsmFilterClient::StartIdentify: Identified as %ls.\n"),
                    (WCHAR *) m_machineName);

            WsbThrow( S_OK );
        } else {
            //
            // Start the identification thread
            //
            if (NULL == m_identifyThread) {
                WsbTrace(OLESTR("CHsmFilterClient::StartIdentify: Starting ID thread.\n"));

                WsbAffirm((m_identifyThread = CreateThread(0, 0, FsaIdentifyThread, (void*) this, 0, &tid)) != 0, HRESULT_FROM_WIN32(GetLastError()));
 
                if (m_identifyThread == NULL) {           
                    WsbAssertHr(E_FAIL);  // TBD What error to return here??
                }
            }
        }

        
    } WsbCatch(hr);


    WsbTraceOut(OLESTR("CFsaFilterClient::StartIdentify"), OLESTR("hr = %ls"), WsbHrAsString(hr));

    return(hr);
}



HRESULT
CFsaFilterClient::Test(
    USHORT* passed,
    USHORT* failed
    )

/*++

Implements:

  IWsbTestable::Test().

--*/
{
    HRESULT     hr = S_OK;

    try {

        WsbAssert(0 != passed, E_POINTER);
        WsbAssert(0 != failed, E_POINTER);

        *passed = 0;
        *failed = 0;

    } WsbCatch(hr);

    return(hr);
}