//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1998-1999.
//
//  File:       DrvNotifArray.cxx
//
//  Contents:   This file contains the class, CDrvNotifArray.
//
//  History:    15-Jul-98 KitmanH     Created
//
//--------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <ciregkey.hxx>
#include <notifary.hxx>

#include <catalog.hxx>
#include <cimbmgr.hxx>
#include <regscp.hxx>
#include <regacc.hxx>
#include "scpfixup.hxx"
#include <catreg.hxx>

//+---------------------------------------------------------------------------
//
//  Member:     CDrvNotifArray::RegisterCatForNotifInRegistry, public
//
//  Synopsis:   Register all the catalogs for device notifications  
//
//  History:    7-15-98   KitmanH   Created
//              8-14-98   KitmanH   Register for notifications for drives
//                                  on which an indexed scope exists too
//
//----------------------------------------------------------------------------

void CDrvNotifArray::RegisterCatForNotifInRegistry()
{
    HKEY hKey;
    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 ) )
            {
                SRegKey xCatNameKey( hCatName );

                // Check if the catalog is inactive and can be ignored

                WCHAR awcKey[MAX_PATH];
                wcscpy( awcKey, wcsRegJustCatalogsSubKey );
                wcscat( awcKey, L"\\" );
                wcscat( awcKey, awcName );
    
                CRegAccess reg( RTL_REGISTRY_CONTROL, awcKey );
                BOOL fInactive = reg.Read( wcsCatalogInactive,
                                           CI_CATALOG_INACTIVE_DEFAULT );

                if ( !fInactive )
                {
                    WCHAR awcPath[MAX_PATH];
                    DWORD cbPath = sizeof awcPath;
                    if ( ERROR_SUCCESS == RegQueryValueEx( hCatName,
                                                           wcsCatalogLocation,
                                                           0,
                                                           0,
                                                           (BYTE *)awcPath,
                                                           &cbPath ) )
                    {
                        AddDriveNotification( awcPath[0] );
                        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() );
                                    
                        CRegistryScopesCallBackAddDrvNotif callback( this );
                        regScopes.EnumerateValues( 0, callback );
                    }
                }
            }
        } while ( TRUE );
    }
} //RegisterCatForNotifInRegistry

//+-------------------------------------------------------------------------
//
//  Member:     UnregisterDeviceNotifications, public
//
//  Synopsis:   Unregister all the device notifications 
//
//  History:    07-07-98   kitmanh       Created.
//
//--------------------------------------------------------------------------

void CDrvNotifArray::UnregisterDeviceNotifications()
{
    ciDebugOut(( DEB_ITRACE, "Unregister Device Notificaiton On Shutdown\n" ));

    CReleasableLock lock( _mutex );

    for ( unsigned i = 0; i < _aDriveNotification.Count(); i++ )
    {
        CDrvNotificationInfo * pDrvNot = _aDriveNotification.Get(i);
        delete pDrvNot;
    }

    _aDriveNotification.Clear();

    // If the async thread is around, get rid of it

    if ( !_xWorker.IsNull() )
    {
        _fAbort = TRUE;
        ciDebugOut(( DEB_ITRACE, "waking up async register for shutdown\n" ));
        _evtWorker.Set();

        lock.Release();

        ciDebugOut(( DEB_ITRACE, "waiting for async register death\n" ));
        _xWorker->WaitForDeath();
        _xWorker.Free();
        _fAbort = FALSE;
    }
} //UnregisterDeviceNotifications

//+-------------------------------------------------------------------------
//
//  Member:     AddDriveNotification, public
//
//  Synopsis:   Register the volume for device notification. Add an entry in
//              the array _aDriveNotification for record
//
//  Arguments:  [wcDriveLetter] -- DriveLetter of the volume where the 
//                                  catalog resides
//
//  History:    18-May-98 KitmanH    Created
//
//--------------------------------------------------------------------------

void CDrvNotifArray::AddDriveNotification( WCHAR wcDriveLetter, BOOL fNotInRegistry )
{
    // first, look up the table to see if the volume is alreay in the table
 
    if ( FindDriveNotification( wcDriveLetter ) )
    {
        ciDebugOut(( DEB_ITRACE, "Volume %wc is already in the DriveNotification array\n",
                     wcDriveLetter ));
        return;
    }

    // if not in the table, check the volume and add an entry of DriveNotificationInfo
    ciDebugOut(( DEB_ITRACE, "CDrvNotifArray::AddDriveNotification: Adding volume %wc to the DriveNot Array\n", 
                 wcDriveLetter ));

    XPtr <CDrvNotificationInfo> xDrvNotif(new CDrvNotificationInfo( wcDriveLetter,
                                                                    fNotInRegistry )); 

    {
        CLock lockx( _mutex );
        
        if ( xDrvNotif->RegisterNotification() ) 
        {
            _aDriveNotification.Add( xDrvNotif.GetPointer(), 
                                     _aDriveNotification.Count() );

            xDrvNotif->CloseVolumeHandle();
            xDrvNotif.Acquire();            

            ciDebugOut(( DEB_ITRACE, "AddDriveNotification succeeded for DRIVE %wc\n", 
                         wcDriveLetter ));
        }
    }
} //AddDriveNotification

//+-------------------------------------------------------------------------
//
//  Function:   FindDriveNotificationByHandle, public
//  
//  Synopsis:   return the device notification for a particular volume (
//              matching the handle). 
//
//  Arguments:  [hNotify] -- handle for volume when a custom device 
//                           notification occurs
//
//  History:    22-Jun-98 KitmanH    Created
//
//--------------------------------------------------------------------------

CDrvNotificationInfo * CDrvNotifArray::FindDriveNotificationByHandle( HDEVNOTIFY hNotify )
{
    CLock lockx( _mutex );
    for ( unsigned i = 0; i < _aDriveNotification.Count(); i++ )
    {
        CDrvNotificationInfo * pDrvNot = _aDriveNotification.Get(i);
        if ( pDrvNot->GethNotify() == hNotify ) 
            return pDrvNot;
    }

    ciDebugOut(( DEB_ITRACE, "Can't find drive notification in array\n" ));
    return 0; 
} //FindDriveNotificationByHandle


//+-------------------------------------------------------------------------
//
//  Function:   FindDriveNotification, public
//  
//  Synopsis:   return the device notification for a particular volume (
//              matching the handle). 
//
//  Arguments:  [wcDrvLetter] -- Drive Letter for a volume
//
//  History:    01-July-98 KitmanH    Created
//
//--------------------------------------------------------------------------

CDrvNotificationInfo * CDrvNotifArray::FindDriveNotification( WCHAR wcDrvLetter )
{
    CLock lockx( _mutex );
    for ( unsigned i = 0; i < _aDriveNotification.Count(); i++ )
    {
        CDrvNotificationInfo * pDrvNot = _aDriveNotification.Get(i);
        if ( toupper(pDrvNot->GetDrvLetter()) == toupper(wcDrvLetter) ) 
            return pDrvNot;
    }

    ciDebugOut(( DEB_ITRACE, "Can't find drive notification in array\n" ));
    return 0; 
} //FindDriveNotification

//+-------------------------------------------------------------------------
//
//  Function:   CDrvNotifArray::RegisterRemovableDrives, public
//  
//  Synopsis:   Registers all removable drives for notification (except a:
//              and b:).
//
//  History:    26-March-99 dlee   Created
//
//--------------------------------------------------------------------------

void CDrvNotifArray::RegisterRemovableDrives()
{
    // Determine which drives exist in this bitmask

    DWORD dwDriveMask = GetLogicalDrives();
    dwDriveMask >>= 2;

    // loop through all the drives c-z

    for ( WCHAR wc = L'C'; wc <= L'Z'; wc++ )
    {
        DWORD dwTemp = ( dwDriveMask & 1 );
        dwDriveMask >>= 1;

        if ( 0 != dwTemp )
        {
            if ( IsRemovableDrive( wc ) )
                AddDriveNotification( wc, TRUE );
        }
    }
} //RegisterRemovableDrives

//+-------------------------------------------------------------------------
//
//  Function:   CDrvNotifArray::RegisterThread, private
//  
//  Synopsis:   The worker thread function.
//
//  History:    12-April-99 dlee   Created
//
//--------------------------------------------------------------------------

DWORD WINAPI CDrvNotifArray::RegisterThread( void * self )
{
    return ( (CDrvNotifArray *) self)->DoRegisterWork();
} //RegisterThread

//+-------------------------------------------------------------------------
//
//  Function:   CDrvNotifArray::RegisterThread, private
//  
//  Synopsis:   Waits around to be told to register for device notificaitons
//              on volumes not currently registered.
//
//  History:    12-April-99 dlee   Created
//
//--------------------------------------------------------------------------

DWORD CDrvNotifArray::DoRegisterWork()
{
    do
    {
        _evtWorker.Wait();

        CLock lock( _mutex );

        ciDebugOut(( DEB_ITRACE, "async register woke up, _fAbort: %d\n", _fAbort ));

        if ( _fAbort )
            break;

        for ( unsigned i = 0; i < _aDriveNotification.Count(); i++ )
        {
            CDrvNotificationInfo & DrvNot = * _aDriveNotification.Get(i);

            if ( !DrvNot.IsRegistered() )
                DrvNot.RegisterNotification();
        }

        _evtWorker.Reset();
    } while (TRUE);

    return 0;
} //DoRegisterWork