/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    adminmanagercache.cxx

Abstract:

    A cache of IAzAuthorizationStore objects

Author:

    Bilal Alam (balam)                  Nov 26, 2001

--*/

#include "precomp.hxx"

BSTR ADMIN_MANAGER::sm_bstrUrlAuthApplication;

//static
HRESULT
ADMIN_MANAGER::Initialize(
    VOID
)
/*++

Routine Description:
    
    Global initialization for ADMIN_MANAGER

Arguments:

    None

Return Value:

    HRESULT

--*/
{   
    //
    // Initialize some BSTRs once
    //
    
    DBG_ASSERT( sm_bstrUrlAuthApplication == NULL );
    
    sm_bstrUrlAuthApplication = SysAllocString( URL_AUTH_APPLICATION_NAME );
    if ( sm_bstrUrlAuthApplication == NULL )
    {
        return HRESULT_FROM_WIN32( GetLastError() );
    }
    
    return NO_ERROR;
}

//static
VOID
ADMIN_MANAGER::Terminate(
    VOID
)
/*++

Routine Description:
    
    Global cleanup for ADMIN_MANAGER

Arguments:

    None

Return Value:

    None

--*/
{
    if ( sm_bstrUrlAuthApplication != NULL )
    {
        SysFreeString( sm_bstrUrlAuthApplication );
        sm_bstrUrlAuthApplication = NULL;
    }
}

HRESULT
ADMIN_MANAGER::Create(
    WCHAR *                 pszStoreName
)
/*++

Routine Description:

    Create a cache entry object.  In this case, the object contains a 
    pointer to a created IAzAuthorizationStore object

Arguments:

    pszStoreName - Name of store to pass down to IAzAdminManager

Return Value:

    HRESULT

--*/
{
    HRESULT             hr;
    BSTR                bstrStoreName;
    VARIANT             vNoParam;
    HANDLE              hToken = NULL;
    BOOL                fRet;
    
    if ( pszStoreName == NULL )
    {
        DBG_ASSERT( FALSE );
        return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
    }
    
    //
    // First create the object
    //
    
    hr = CoCreateInstance( CLSID_AzAuthorizationStore,
                           NULL, 
                           CLSCTX_SERVER, 
                           IID_IAzAuthorizationStore,
                           (VOID**) &_pAdminManager );
    if ( FAILED( hr ) )
    {
        return hr;
    }
    
    DBG_ASSERT( _pAdminManager != NULL );

    //
    // Is this an XML or AD URL?  (incredibly lame!)
    //
    
    //
    // Do some BSTR nonsence
    //
    
    bstrStoreName = SysAllocString( pszStoreName );
    if ( bstrStoreName == NULL )
    {
        return HRESULT_FROM_WIN32( GetLastError() );
    }
    
    _wcsupr( (WCHAR*) bstrStoreName );
    
    //
    // Now initialize the AdminManager
    //

    VariantInit( &vNoParam );
    vNoParam.vt = VT_ERROR;
    vNoParam.scode = DISP_E_PARAMNOTFOUND;

    //
    // Initialize() routine must be called unimpersonated.  Very lame!
    //

    fRet = OpenThreadToken( GetCurrentThread(),
                            TOKEN_IMPERSONATE,
                            TRUE,
                            &hToken );
    if ( fRet )
    {
        DBG_ASSERT( hToken != NULL );
        RevertToSelf();
    }
    
    hr = _pAdminManager->Initialize( 0,
                                     bstrStoreName,
                                     vNoParam );
    
    if ( hToken != NULL )
    {
        if ( !SetThreadToken( NULL, hToken ) )
        {
            DBG_ASSERT( FALSE );
        }
        
        CloseHandle( hToken );
        hToken = NULL;
    }

    SysFreeString( bstrStoreName );

    if ( FAILED( hr ) )
    {
        return hr;
    }
    
    _fInitialized = TRUE;
    
    return NO_ERROR;
}

HRESULT
ADMIN_MANAGER::GetApplication(
    AZ_APPLICATION **           ppAzApplication
)
/*++

Routine Description:

    Retrieve the AZ_APPLICATION from the given admin maanger.  In the future
    there may be more than one application associated with admin manager.  
    For now there isn't.  

Arguments:

    ppApplication - Receives pointer to AZ_APPLICATION on success

Return Value:

    HRESULT

--*/
{
    HRESULT                 hr;
    AZ_APPLICATION *        pAzApplication;
    IAzApplication *        pIApplication;
    VARIANT                 vNoParam;
    
    if ( ppAzApplication == NULL )
    {
        DBG_ASSERT( FALSE );
        return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
    }
    
    *ppAzApplication = NULL;
    
    //
    // If we already have one associated, then we're done
    //
   
    if ( _pAzApplication != NULL )
    {
        *ppAzApplication = _pAzApplication;
        return NO_ERROR;
    }
    
    //
    // OK.  We'll have to create one now
    //
    
    //
    // Get the application we're interested in (URLAuth)
    //
    
    DBG_ASSERT( _pAdminManager != NULL );

    VariantInit( &vNoParam );
    vNoParam.vt = VT_ERROR;
    vNoParam.scode = DISP_E_PARAMNOTFOUND;
    
    hr = _pAdminManager->OpenApplication( sm_bstrUrlAuthApplication,
                                          vNoParam,
                                          &pIApplication );
    if ( FAILED( hr ) )
    {
        return hr;
    }
    
    DBG_ASSERT( pIApplication != NULL );
    
    //
    // Create an AZ_APPLICATION object which wraps the IAzApplication.  In
    // the future, an ADMIN_MANAGER may hold more than one application.  For
    // now it won't
    //
    
    pAzApplication = new AZ_APPLICATION( pIApplication );
    if ( pAzApplication == NULL )
    {
        pIApplication->Release();
        
        return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
    }
    
    hr = pAzApplication->Create();
    if ( FAILED( hr ) )
    {
        delete pAzApplication;
        return hr;
    }
    
    //
    // Finally, set the AZ_APPLICATION 
    //
    
    LockCacheEntry();
    
    if ( _pAzApplication == NULL )
    {
        _pAzApplication = pAzApplication;
    }
    else
    {
        delete pAzApplication;
    }
    
    UnlockCacheEntry();
    
    *ppAzApplication = _pAzApplication;
    
    DBG_ASSERT( *ppAzApplication != NULL );
    
    return NO_ERROR;
}

HRESULT
ADMIN_MANAGER_CACHE::GetAdminManager(
    WCHAR *                 pszStoreName,
    ADMIN_MANAGER **        ppAdminManager
)
/*++

Routine Description:
    
    Returns (and creates if necessary) an ADMIN_MANAGER object for the 
    given store name

Arguments:

    pszStoreName - Store name
    ppAdminManager - Filled with pointer to ADMIN_MANAGER if successful

Return Value:

    HRESULT

--*/
{
    ADMIN_MANAGER_CACHE_KEY         cacheKey;
    ADMIN_MANAGER *                 pAdminManager = NULL;
    HRESULT                         hr;
    
    if ( pszStoreName == NULL ||
         ppAdminManager == NULL )
    {
        DBG_ASSERT( FALSE );
        return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
    }
    
    *ppAdminManager = NULL;
    
    //
    // First lookup in the cache
    //
    
    hr = cacheKey.SetStore( pszStoreName );
    if ( FAILED( hr ) )
    {
        return hr;
    }

TryAgain:
    
    hr = FindCacheEntry( &cacheKey,
                         (CACHE_ENTRY**) &pAdminManager );
    if ( FAILED( hr ) )
    {
        //
        // Anything but ERROR_FILE_NOT_FOUND is a bad thing
        //
        
        if ( hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
        {
            return hr;
        }
        
        //
        // Create an ADMIN_MANAGER
        //
        
        pAdminManager = new ADMIN_MANAGER( this );
        if ( pAdminManager == NULL )
        {
            return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
        }
        
        //
        // We'll initialize it later.  We do this to avoid more than one 
        // simulatenous call to pAdminManager->Create() because AZRoles crap
        // doesn't like it
        //
        
        //
        // Add it to the cache.  If we couldn't because it was already
        // there, then try again to retrieve it
        //
        
        AddCacheEntry( (CACHE_ENTRY*) pAdminManager );

        if ( pAdminManager->QueryCached() == FALSE )
        {
            pAdminManager->DereferenceCacheEntry();
            pAdminManager = NULL;
            goto TryAgain;
        }
    }
    
    //
    // Initialize the entry once before use.  This mechanism ensures only
    // one call is made to initialize for the same store name
    //
    
    if ( !pAdminManager->QueryIsInitialized() )
    {
        pAdminManager->LockCacheEntry();

        if ( !pAdminManager->QueryIsInitialized() )
        {
            hr = pAdminManager->Create( pszStoreName );
        }
        else
        {
            hr = NO_ERROR;
        }
       
        pAdminManager->UnlockCacheEntry(); 
        
        if ( FAILED( hr ) )
        {
            pAdminManager->DereferenceCacheEntry();
            pAdminManager = NULL;
            return hr;
        }
    }
    
    DBG_ASSERT( pAdminManager != NULL );
    
    *ppAdminManager = pAdminManager;
    
    return NO_ERROR;
}