//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1999.
//
//  File:       dslookup.cxx
//
//  Contents:   DocStoreLookUp code
//
//  Classes:    CClientDocStoreLocator
//
//  History:    1-16-97   srikants   Created
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

// for definition of CRequestQueue
#include <query.hxx>
#include <srequest.hxx>

#include <dslookup.hxx>
#include <dbprputl.hxx>
#include <catarray.hxx>
#include <docstore.hxx>
#include <imprsnat.hxx>
#include <lang.hxx>
#include <ciole.hxx>
#include <fsci.hxx>
#include <acinotfy.hxx>
#include <cicat.hxx>
#include <regacc.hxx>
#include <ciregkey.hxx>
#include <drvnotif.hxx>
#include <driveinf.hxx>

#include <regscp.hxx>
#include <catreg.hxx>
#include <removcat.hxx>

extern CCatArray Catalogs;
extern void OpenCatalogsInRegistry( BOOL fOpenForReadyOnly = FALSE );

//+---------------------------------------------------------------------------
//
//  Member:     CClientDocStore::QueryInterface
//
//  Synopsis:   Supports IID_IUnknown
//              IID_ICiCDocStoreLocator
//
//  History:    12-03-96   srikants   Created
//
//----------------------------------------------------------------------------


STDMETHODIMP CClientDocStoreLocator::QueryInterface(
    REFIID riid,
    void **ppvObject)
{
    Win4Assert( 0 != ppvObject );

    if ( IID_ICiCDocStoreLocator == riid )
        *ppvObject = (void *)((ICiCDocStoreLocator *)this);
    else if ( IID_IUnknown == riid )
        *ppvObject = (void *)((IUnknown *) (ICiCDocStore *)this);
    else
    {
        *ppvObject = 0;
        return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;
} //QueryInterface

//+---------------------------------------------------------------------------
//
//  Member:     CClientDocStoreLocator::AddRef
//
//  History:    12-03-96   srikants   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CClientDocStoreLocator::AddRef()
{
    return InterlockedIncrement(&_refCount);
} //AddRef

//+---------------------------------------------------------------------------
//
//  Member:     CClientDocStoreLocator::Release
//
//  History:    12-03-96   srikants   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CClientDocStoreLocator::Release()
{
    Win4Assert( _refCount > 0 );

    ciDebugOut(( DEB_ITRACE, "DocStoreLocator::Release.. _refCount == %d\n", _refCount ));
    LONG refCount = InterlockedDecrement(&_refCount);

    if (  refCount <= 0 )
        delete this;

    return refCount;
} //Release

//+---------------------------------------------------------------------------
//
//  Member:     CClientDocStoreLocator::LookUpDocStore
//
//  Synopsis:   Locates the docStore that is specified in the db properties
//              and returns its pointer (if located).
//
//  Arguments:  [pIDBProperties] - 
//              [ppICiCDocStore] -
//              [fMustAlreadyExist] -- If TRUE, the docstore must already
//                                     be opened, or the call fails.
//
//  Returns:    S_OK if found;
//              CI_E_DOCSTORE_NOT_FOUND if not located.
//
//  History:    1-16-97   srikants   Created
//
//----------------------------------------------------------------------------


STDMETHODIMP CClientDocStoreLocator::LookUpDocStore(
    IDBProperties * pIDBProperties,
    ICiCDocStore ** ppICiCDocStore,
    BOOL            fMustAlreadyExist )
{
    SCODE sc = S_OK;

    TRY
    {
        CGetDbProps connectProps;

        connectProps.GetProperties( pIDBProperties,
                                    CGetDbProps::eCatalog|
                                    CGetDbProps::eScopesAndDepths );
    
        // if a catalog was passed (as a guess or actual), try to open it

        WCHAR const * pwcCatalog = connectProps.GetCatalog();

        //
        // Prevent a hacker from passing a bogus path name -- we trash
        // stack and/or AV in this circumstance!
        //

        if ( 0 == pwcCatalog )
            THROW( CException( CI_E_NOT_FOUND ) );

        unsigned cwc = wcslen( pwcCatalog );
        if ( ( 0 == cwc ) || ( cwc >= ( MAX_PATH - 1 ) ) )
            THROW( CException( CI_E_NOT_FOUND ) );

        CClientDocStore * pDocStore = 0;

        if ( 0 != pwcCatalog )
            pDocStore = Catalogs.GetDocStore( pwcCatalog, fMustAlreadyExist );

        if ( 0 != pDocStore )
        {
            sc = pDocStore->QueryInterface( IID_ICiCDocStore,
                                            (void **) ppICiCDocStore );
        }
        else
        {
            // special case: adminstration connection without a docstore associated

            if ( !wcscmp(CIADMIN, pwcCatalog) )
            {
                ciDebugOut(( DEB_ITRACE, 
                             "CClientDocStoreLocator::LookUpDocStore.. ADMINSTRATION connection is requested\n" ));
                sc = CI_S_NO_DOCSTORE;
            }
            else
                sc = CI_E_NOT_FOUND;            
        }
    }
    CATCH( CException,e )
    {
        sc = e.GetErrorCode();    
    }
    END_CATCH

    return sc;
} //LookUpDocStore

//+---------------------------------------------------------------------------
//
//  Member:     CClientDocStoreLocator::Shutdown
//
//  Synopsis:   Shuts down the content index by closing all open catalogs.
//
//  History:    1-29-97   srikants   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CClientDocStoreLocator::Shutdown()
{
    ciDebugOut(( DEB_ITRACE, "DocStoreLocator::Shutdown is called\n" ));
    return FsCiShutdown();
}

//+---------------------------------------------------------------------------
//
//  Function:   FsCiShutdown
//
//  Synopsis:   Does shutdown processing for the FsCi component.
//
//  History:    2-27-97   srikants   Created
//
//----------------------------------------------------------------------------

SCODE FsCiShutdown()
{
    SCODE sc = S_OK;

    TRY
    {
        Catalogs.Flush();
        CCiOle::Shutdown();
        g_LogonList.Empty();        
        TheFrameworkClientWorkQueue.Shutdown();
    }
    CATCH( CException,e )
    {
        sc = e.GetErrorCode();

        // If this assert hits, we took an exception while releasing
        // resources, which isn't allowed to happen.  It's a bug elsewhere.

        Win4Assert( !"FsCiShutdown failed, and it isn't allowed to" );
    }
    END_CATCH

    return sc;
} //FsCiShutdown

//+-------------------------------------------------------------------------
//
//  Member:     CClientDocStoreLocator::OpenAllDocStores
//
//  Synopsis:   Opens all the catalogs in the registry
//
//  History:    06-May-98   kitmanh       Created.
//
//--------------------------------------------------------------------------

STDMETHODIMP CClientDocStoreLocator::OpenAllDocStores()
{
    SCODE sc = S_OK;

    TRY
    {
        OpenCatalogsInRegistry();
    }
    CATCH( CException,e )
    {
        sc = e.GetErrorCode();    
    }
    END_CATCH

    return sc;
} //OpenAllDocStores

//+-------------------------------------------------------------------------
//
//  Member:     CClientDocStoreLocator::GetDocStoreState
//
//  Synopsis:   Gets the state of a docstore
//              (used when restarting a stopped catalog) or the directory is
//              unwritable
//
//  Arguments:  [pwcDocStore]   -- Name of the catalog
//              [ppICiDocStore] -- Returns the docstore
//              [pdwState]      -- Returns the state
//
//  History:    06-May-98   kitmanh       Created.
//
//--------------------------------------------------------------------------

STDMETHODIMP CClientDocStoreLocator::GetDocStoreState( 
    WCHAR const *   pwcDocStore, 
    ICiCDocStore ** ppICiCDocStore,
    DWORD *         pdwState )
{
    SCODE sc = S_OK;

    TRY
    {
        if ( Catalogs.IsCatStopped( pwcDocStore ) )
        {
            *pdwState = CICAT_STOPPED;
            sc = CI_S_CAT_STOPPED;
            return sc;
        }
        
        CClientDocStore * pDocStore = 0;

        if ( 0 != pwcDocStore )
            pDocStore = Catalogs.GetDocStore( pwcDocStore );
  
        if ( 0 != pDocStore )
        {
            Win4Assert( pDocStore );

            // get the interface 
            sc = pDocStore->QueryInterface( IID_ICiCDocStore,
                                            (void **) ppICiCDocStore );
            
            // get the oldstate and flag

            CiCat * pCiCat = pDocStore->GetCiCat();

            // Is this the null catalog?

            if ( 0 == pCiCat )
                THROW( CException( CI_E_NOT_FOUND ) );
            
            if ( pCiCat->IsReadOnly() ) 
            {
                ciDebugOut(( DEB_ITRACE, "CClientDocStoreLocator::GetDocStoreState.. CiCatReadOnly == %d\n", 
                             pCiCat->IsReadOnly() ));
                *pdwState = CICAT_READONLY;
            }
            else
                *pdwState = CICAT_WRITABLE;
            
            BOOL fNoQuery;
            pDocStore->IsNoQuery( &fNoQuery );
            if ( fNoQuery ) 
               *pdwState |= CICAT_NO_QUERY; 
        }
        else 
            sc = CI_E_NOT_FOUND; //or some other error?
    }
    CATCH( CException, e )
    {
        sc = e.GetErrorCode();    
    }
    END_CATCH

    return sc;
} //GetDocStoreState

BOOL IsDirectoryWritable( WCHAR const * pwcPath );

//+-------------------------------------------------------------------------
//
//  Member:     CClientDocStoreLocator::IsMarkedReadOnly
//
//  Synopsis:   Check if the catalog is marked for readonly in the registry
//              (used when restarting a stopped catalog) or the directory is
//              unwritable
//
//  Arguments:  [wcsCat] -- Name of the catalog
//              [pfReadOnly] -- output
//
//  History:    06-May-98   kitmanh       Created.
//
//--------------------------------------------------------------------------

STDMETHODIMP CClientDocStoreLocator::IsMarkedReadOnly( WCHAR const * wcsCat, BOOL * pfReadOnly )
{
    SCODE sc = S_OK;

    TRY
    {
        unsigned cwcNeeded = wcslen( wcsRegJustCatalogsSubKey );
        cwcNeeded += 2; // "\\" + null termination
        cwcNeeded += wcslen( wcsCat );
    
        XArray<WCHAR> xKey( cwcNeeded );
        wcscpy( xKey.Get(), wcsRegJustCatalogsSubKey );
        wcscat( xKey.Get(), L"\\" );
        wcscat( xKey.Get(), wcsCat );
    
        CRegAccess reg( RTL_REGISTRY_CONTROL, xKey.Get() );

        BOOL fReadOnly = FALSE;
        *pfReadOnly = reg.Read(wcsIsReadOnly, fReadOnly );
        ciDebugOut(( DEB_ITRACE, "IsMarkedReadOnly is %d\n", *pfReadOnly ));
    }
    CATCH( CException,e )
    {
        sc = e.GetErrorCode();    
    }
    END_CATCH

    return sc;
} //IsMarkedReadOnly

//+-------------------------------------------------------------------------
//
//  Member:     IsVolumeOrDirRO
//
//  Synopsis:   Check if the volume and the directory are unwritable
//
//  Arguments:  [wcsCat] -- Name of the catalog
//              [pfReadOnly] -- output
//
//  History:    07-May-98   kitmanh       Created.
//
//--------------------------------------------------------------------------
STDMETHODIMP CClientDocStoreLocator::IsVolumeOrDirRO( WCHAR const * wcsCat, 
                                                      BOOL * pfReadOnly )
{
    SCODE sc = S_OK;
    *pfReadOnly = FALSE;

    TRY
    {
        WCHAR wcsKey[MAX_PATH];
        wcscpy( wcsKey, wcsRegCatalogsSubKey );
        wcscat( wcsKey, L"\\" );

        unsigned cwc = wcslen( wcsKey ) + wcslen( wcsCat );

        if ( cwc >= MAX_PATH )
            THROW( CException( E_INVALIDARG ) );

        wcscat( wcsKey, wcsCat );

        HKEY hKey;
        if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                                            wcsKey,
                                            0,
                                            KEY_QUERY_VALUE,
                                            &hKey ) )
        {
            SRegKey xKey( hKey );

            WCHAR awcPath[MAX_PATH];
            DWORD cbPath = sizeof awcPath;
            if ( ERROR_SUCCESS == RegQueryValueEx( hKey,
                                                   wcsCatalogLocation,
                                                   0,
                                                   0,
                                                   (BYTE *)awcPath,
                                                   &cbPath ) )
            {
                CDriveInfo driveInfo ( awcPath, 0 );
                wcscat( awcPath, L"Catalog.wci" );  //is there a constant for this?
                ciDebugOut(( DEB_ITRACE, "IsVolumeOrDirRO.. awcPath == %ws\n", awcPath ));
                ciDebugOut(( DEB_ITRACE, "Volume Writeprotected is %d\n", driveInfo.IsWriteProtected() ));
                ciDebugOut(( DEB_ITRACE, "Diretory Writable is %d\n", IsDirectoryWritable( awcPath ) )); 
                *pfReadOnly = ( driveInfo.IsWriteProtected() || !( IsDirectoryWritable( awcPath ) ) );
            }
        }
    }
    CATCH( CException,e )
    {
        sc = e.GetErrorCode();    
    }
    END_CATCH

    return sc;
} //IsVolumeOrDirRO

//+---------------------------------------------------------------------------
//
//  Member:     StopCatalogsOnVol
//
//  Synopsis:   Stops all catalogs on the volume specified.
//
//  Arguments:  [wcVol] -- Volume letter 
//              [pRequestQ] -- Pointer to the RequestQueue
//
//  History:    07-05-98    kitmanh   Created
//              07-20-98    kitmanh   Stop the catalogs with scopes on 
//                                    volume being locked too
//
//----------------------------------------------------------------------------

STDMETHODIMP CClientDocStoreLocator::StopCatalogsOnVol( WCHAR wcVol, 
                                                        void * pRequestQ )          
{
    ciDebugOut(( DEB_ITRACE, "StopCatalogsOnVol %wc\n", wcVol ));

    //enumerate reg to find out who needs to stop
    //add a workitem for all of them 
        
    ciDebugOut(( DEB_ITRACE, "StopCatalogsOnVol is called\n" ));
    Win4Assert( 0 != pRequestQ );

    CRequestQueue * pRequestQueue = (CRequestQueue *)pRequestQ;
    SCWorkItem newItem;
    HKEY hKey;
    SCODE sc = S_OK;
    BOOL fFiledWorkItem = FALSE;
    
    TRY
    {
        if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                                            wcsRegCatalogsSubKey,
                                            0,
                                            KEY_QUERY_VALUE |
                                            KEY_ENUMERATE_SUB_KEYS,
                                            &hKey ) )
        {
            SRegKey xKey( hKey );
            DWORD iSubKey = 0;

            do
            {
                FILETIME ft;
                WCHAR awcName[MAX_PATH];
                DWORD cwcName = sizeof awcName / sizeof WCHAR;
                LONG err = RegEnumKeyEx( hKey,
                                         iSubKey,
                                         awcName,
                                         &cwcName,
                                         0, 0, 0, &ft );

                // either error or end of enumeration

                if ( ERROR_SUCCESS != err )
                    break;

                iSubKey++;

                HKEY hCatName;
                if ( ERROR_SUCCESS == RegOpenKeyEx( hKey,
                                                    awcName,
                                                    0,
                                                    KEY_QUERY_VALUE,
                                                    &hCatName ) )
                {
                    // enumerate the location registries
                    SRegKey xCatNameKey( hCatName );


                    // Check if the catalog is inactive and can be ignored
   
                    WCHAR awcKey[MAX_PATH];
                    wcscpy( awcKey, wcsRegJustCatalogsSubKey );
                    wcscat( awcKey, L"\\" );

                    unsigned cwc = wcslen( awcKey ) + wcslen( awcName );

                    if ( cwc >= MAX_PATH )
                        THROW( CException( E_INVALIDARG ) );

                    wcscat( awcKey, awcName );
    
                    CRegAccess reg( RTL_REGISTRY_CONTROL, awcKey );
                    BOOL fInactive = reg.Read( wcsCatalogInactive,
                                               CI_CATALOG_INACTIVE_DEFAULT );

                    BOOL fIsAutoMount = reg.Read( wcsIsRemovableCatalog, (ULONG) FALSE );

                    if ( !fInactive )
                    {
                        WCHAR awcPath[MAX_PATH];
                        DWORD cbPath = sizeof awcPath;
                        if ( ERROR_SUCCESS == RegQueryValueEx( hCatName,
                                                               wcsCatalogLocation,
                                                               0,
                                                               0,
                                                               (BYTE *)awcPath,
                                                               &cbPath ) )
                        {
                            if ( toupper(awcPath[0]) == toupper(wcVol) ) 
                            {
                                //check old state of docstore
                                DWORD dwOldState;
                                XInterface<ICiCDocStore> xDocStore;
                                    
                                sc = GetDocStoreState( awcName,
                                                       xDocStore.GetPPointer(),
                                                       &dwOldState );
                           
                                if ( SUCCEEDED(sc) )
                                {
                                    ciDebugOut(( DEB_ITRACE, "StopCatalogsOnVol: dwOldState is %d for catalog %ws\n",
                                                 dwOldState, awcName ));
    
                                    if ( 0 == (CICAT_STOPPED & dwOldState) )
                                    {
                                        ciDebugOut(( DEB_ITRACE, "CATALOG %ws WAS NOT STOPPED BEFORE\n", awcName ));
                                        ciDebugOut(( DEB_ITRACE, "Add old state %d\n", dwOldState ));

                                        if ( !fIsAutoMount )
                                            Catalogs.AddStoppedCat( dwOldState, awcName, wcVol );
                                        
                                        newItem.type = eStopCat;
                                        newItem.pDocStore = xDocStore.GetPointer();
                                    
                                        pRequestQueue->AddSCItem( &newItem , 0 );
                                        fFiledWorkItem = TRUE;
                                    }
                                    else
                                    {
                                        ciDebugOut(( DEB_ITRACE, "CATALOG %ws WAS STOPPED BEFORE\n", awcName ));
                                        BOOL fSucceeded = Catalogs.IncStopCount( awcName, wcVol );
                                        Win4Assert( fSucceeded );
                                    }
                                }
                            }
                            else  // enumerate the scopes to see if this catalog needs to stop
                            {
                                unsigned cwcNeeded = wcslen( wcsRegJustCatalogsSubKey );
                                cwcNeeded += 3; // "\\" x 2 + null termination
                                cwcNeeded += wcslen( awcName );
                                cwcNeeded += wcslen( wcsCatalogScopes );
                                    
                                XArray<WCHAR> xKey( cwcNeeded );
                                wcscpy( xKey.Get(), wcsRegJustCatalogsSubKey );
                                wcscat( xKey.Get(), L"\\" );
                                wcscat( xKey.Get(), awcName );
                                wcscat( xKey.Get(), L"\\" );
                                wcscat( xKey.Get(), wcsCatalogScopes );
                                    
                                CRegAccess regScopes( RTL_REGISTRY_CONTROL, xKey.Get() );
                                
                                CRegistryScopesCallBackToDismount callback( wcVol );
                                regScopes.EnumerateValues( 0, callback );
    
                                if ( callback.WasFound() )
                                {
                                    //check old state of docstore
                                    DWORD dwOldState;
                                    XInterface<ICiCDocStore> xDocStore;
                            
                                    sc = GetDocStoreState( awcName,
                                                           xDocStore.GetPPointer(),
                                                           &dwOldState );
    
                                    if ( SUCCEEDED(sc) )
                                    {
                                        if ( 0 == (CICAT_STOPPED & dwOldState) )
                                        {
                                            ciDebugOut(( DEB_ITRACE, "CATALOG %ws WAS NOT STOPPED BEFORE\n", awcName ));
                                            ciDebugOut(( DEB_ITRACE, "Creating an SCItem\n" ));

                                            if ( !fIsAutoMount )
                                                Catalogs.AddStoppedCat( dwOldState, awcName, wcVol );
                                            
                                            newItem.type = eStopCat;
                                            newItem.pDocStore = xDocStore.GetPointer();
                                            
                                            pRequestQueue->AddSCItem( &newItem , 0 );
                                            fFiledWorkItem = TRUE;
                                        }
                                        else
                                        {
                                            ciDebugOut(( DEB_ITRACE, "CATALOG %ws WAS STOPPED BEFORE\n", awcName ));
                                            BOOL fSucceeded = Catalogs.IncStopCount( awcName, wcVol );
                                            Win4Assert( fSucceeded );
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } while ( TRUE );
        }

        //
        // If we filed a workitem to close a temporary catalog, delete
        // its registry entries.
        //

        if ( fFiledWorkItem )
        {
            //
            // If this is an auto-mount catalog, delete the temporary
            // registry entries.
            //

            if ( IsRemovableDrive( wcVol ) )
            {
                CRemovableCatalog cat( wcVol );
                cat.Destroy();
            }
        }
        else
        {
            ciDebugOut(( DEB_ITRACE, "no catalogs to stop on %wc\n", wcVol ));

            //
            // File a fake work item so we don't force closeed connections on
            // all docstores.  Otherwise queries will be aborted for no
            // reason.
            //

            newItem.type = eNoCatWork;
            newItem.pDocStore = (ICiCDocStore*)(~0);
            pRequestQueue->AddSCItem( &newItem , 0 );
        }
    }
    CATCH( CException,e )
    {
        sc = e.GetErrorCode();    
    }
    END_CATCH

    return sc;
} //StopCatalogsOnVol

//+---------------------------------------------------------------------------
//
//  Member:     CClientDocStoreLocator::StartCatalogsOnVol
//
//  Synopsis:   Restore all catalogs on the volume specified to its previous
//              state before the volume was locked.
//
//  Arguments:  [wcVol] -- Volume letter 
//              [pRequestQ] -- Pointer to the RequestQueue
//
//  History:    07-07-98    kitmanh   Created
//              07-23-98    kitmanh   Restores catalogs from StoppedArray,
//                                    instead of enumerating registry
//              09-03-98    kitmanh   Delegated the work to CatArray
//
//----------------------------------------------------------------------------

STDMETHODIMP CClientDocStoreLocator::StartCatalogsOnVol( WCHAR wcVol, 
                                                         void * pRequestQ )
{
    Win4Assert( 0 != pRequestQ );

    CRequestQueue * pRequestQueue = (CRequestQueue *)pRequestQ;
    
    SCODE sc = S_OK;
    
    TRY
    {
        Catalogs.StartCatalogsOnVol( wcVol, pRequestQueue );
    }
    CATCH( CException,e )
    {
        sc = e.GetErrorCode();    
    }
    END_CATCH

    return sc;
} //StartCatalogsOnVol

//+-------------------------------------------------------------------------
//
//  Member:     CClientDocStoreLocator::AddStoppedCat, public
//  
//  Synopsis:   Add an item of COldCatState into the _aStopCatalogs array
//
//  Arguments:  [dwOldState] -- Old state of a docstore
//              [wcsCatName] -- Catalog name of the docstore
//
//  History:    16-July-98 KitmanH    Created
//
//--------------------------------------------------------------------------

STDMETHODIMP CClientDocStoreLocator::AddStoppedCat( DWORD dwOldState, 
                                                    WCHAR const * wcsCatName )
{
    SCODE sc = S_OK;
    
    TRY
    {
        Catalogs.AddStoppedCat( dwOldState, wcsCatName, 0 );
    }
    CATCH( CException,e )
    {
        sc = e.GetErrorCode();    
    }
    END_CATCH

    return sc;
} //AddStoppedCat