/*++

Copyright (c) 1998  Microsoft Corporation

Module Name:

    dirscan.c

Abstract:

    Implementation of directory scanner.

Author:

    Wesley Witt (wesw) 18-Dec-1998

Revision History:

    Andrew Ritz (andrewr) 7-Jul-1999 : added comments

--*/

#include "sfcp.h"
#pragma hdrstop


NTSTATUS
SfcDoScan(
    IN PSCAN_PARAMS ScanParams
    )

/*++

Routine Description:

    Scan the set of protected DLLs and compare them with the cached versions.
    If any are different, copy the correct one back.

Arguments:

    ScanParams - pointer to SCAN_PARAMS structure indicating scanning behavior
                 (such as whether to display UI or not)

Return Value:

    NTSTATUS code of any fatal error.

--*/

{
    NTSTATUS StatusPopulate, StatusSxsScan, rStatus;

    StatusPopulate = SfcPopulateCache( ScanParams->ProgressWindow, TRUE, ScanParams->AllowUI, NULL ) ? 
        STATUS_SUCCESS : STATUS_UNSUCCESSFUL;

    StatusSxsScan = SfcDoForcedSxsScan( ScanParams->ProgressWindow, TRUE, ScanParams->AllowUI );

    // Figure out which of these two failed.  We really do need to do both, rather than
    // just returning after a check of the SfcPopulateCache call.
    if ( !NT_SUCCESS( StatusPopulate ) ) {
        rStatus = StatusPopulate;
        DebugPrint1( LVL_MINIMAL, L"Failed scanning SFC: 0x%08x\n", rStatus );
    } else if ( !NT_SUCCESS( StatusSxsScan ) ) {
        rStatus = StatusSxsScan;
        DebugPrint1( LVL_MINIMAL, L"Failed scanning SxS: 0x%08x\n", rStatus );
    } else {
        rStatus = STATUS_SUCCESS;
    }

    return rStatus;
}


INT_PTR
CALLBACK
ProgressDialogProc(
    HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    UNREFERENCED_PARAMETER( lParam );

    if (uMsg == WM_INITDIALOG) {
        if (hEventScanCancel == NULL) {
            EnableWindow( GetDlgItem(hwndDlg,IDCANCEL), FALSE );
        }
        CenterDialog( hwndDlg );
        ShowWindow( hwndDlg, SW_SHOWNORMAL );
        UpdateWindow( hwndDlg );
        SetForegroundWindow( hwndDlg );
        return TRUE;
    }
    if (uMsg == WM_COMMAND && LOWORD(wParam) == IDCANCEL) {
        SetEvent( hEventScanCancel );
        EndDialog( hwndDlg, 0 );
    }
    return FALSE;
}


NTSTATUS
SfcScanProtectedDlls(
    PSCAN_PARAMS ScanParams
    )
/*++

Routine Description:

    Thread routine to scan for protected dlls on the system.  The routine
    creates a dialog so the user can tell what's going on (if requested) and
    then calls into the main scanning routine.

Arguments:

    ScanParams - pointer to SCAN_PARAMS structure indicating scanning behavior
                 (such as whether to display UI or not)

Return Value:

    NTSTATUS code indicating outcome.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    HWND hDlg = NULL;
#if 0
    HDESK hDesk = NULL;
#endif
    HANDLE hThread;

    ASSERT( ScanParams != NULL );

    //
    // make sure we only kick off one of these at a time
    //
    if (ScanInProgress) {
        if (ScanParams->FreeMemory) {
            MemFree( ScanParams );
        }
        return(ERROR_IO_PENDING);
    }


    //
    // if the system is configured to show UI progress and we don't
    // have a progress window, then we need to create a new thread
    // to do the scan as well as a progress dialog.
    //
    if (SfcQueryRegDwordWithAlternate(REGKEY_POLICY, REGKEY_WINLOGON, REGVAL_SFCSHOWPROGRESS, 1) &&
        ScanParams->ProgressWindow == NULL &&
        0 == m_gulAfterRestore) {
        //
        // if the user isn't logged in, we need to wait for them to do so
        // before thinking about creating a dialog
        //
        if (!UserLoggedOn) {
            Status = NtWaitForSingleObject(hEventLogon,TRUE,NULL);
            if (!NT_SUCCESS(Status)) {
                DebugPrint1(LVL_MINIMAL, L"Failed waiting for the logon event, ec=0x%08x",Status);
            }
        }

        //
        // we need access to the user's desktop now that they're logged in
        //
#if 0
        hDesk = OpenInputDesktop( 0, FALSE, MAXIMUM_ALLOWED );
        if ( hDesk ) {
            SetThreadDesktop( hDesk );
            CloseDesktop( hDesk );
        } else {
            DebugPrint1(LVL_MINIMAL, L"OpenInputDesktop failed, ec=0x%08x",GetLastError());
        }
#else
        SetThreadDesktop( hUserDesktop );
#endif

        //
        // create an event so the user can cancel the scan
        //
        // (note that we should only have one scan going on at any given
        // time or our cancel object can get out of sync)
        //
        ASSERT( hEventScanCancel == NULL );
        ASSERT( hEventScanCancelComplete == NULL);
        hEventScanCancel = CreateEvent( NULL, FALSE, FALSE, NULL );
        hEventScanCancelComplete = CreateEvent( NULL, FALSE, FALSE, NULL );

        //
        // create the dialog the user will see UI in
        //
        hDlg = CreateDialog(
            SfcInstanceHandle,
            MAKEINTRESOURCE(IDD_PROGRESS),
            NULL,
            ProgressDialogProc
            );
        if (hDlg) {
            //
            // scale the progress dialog (we assume that it takes the same
            // amount of time to scan each file in the system)
            //
            ScanParams->ProgressWindow = GetDlgItem( hDlg, IDC_PROGRESS );
            SendMessage( ScanParams->ProgressWindow, PBM_SETRANGE, 0, MAKELPARAM(0,SfcProtectedDllCount) );
            SendMessage( ScanParams->ProgressWindow, PBM_SETPOS, 0, 0 );
            SendMessage( ScanParams->ProgressWindow, PBM_SETSTEP, 1, 0 );

            //
            // create a thread to do the work so we can pump messages in this
            // thread that already has access to the desktop
            //
            hThread = CreateThread(
                NULL,
                0,
                (LPTHREAD_START_ROUTINE)SfcDoScan,
                ScanParams,
                0,
                NULL
                );
            if (hThread) {
                MSG msg;
                while(1) {
                    //
                    // pump messages until the "worker" thread goes away or the
                    // dialog ends
                    //
                    if (WAIT_OBJECT_0+1 == MsgWaitForMultipleObjects( 1, &hThread, FALSE, INFINITE, QS_ALLEVENTS )) {
                        while (PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) {
                            if (!IsDialogMessage( hDlg, &msg )) {
                                TranslateMessage (&msg);
                                DispatchMessage (&msg);
                            }
                        }
                    } else {
                        break;
                    }
                }
                CloseHandle( hThread );
                EndDialog( hDlg, 0 );
            } else {
                //
                // CreateThread failed... kill the dialog and try to do it
                // synchronously
                //
                EndDialog( hDlg, 0 );
                SfcDoScan( ScanParams );
            }
        } else {
            //
            // CreateDialog failed... just try to do it synchronously
            //
            SfcDoScan( ScanParams );
        }

        //
        // cleanup
        //
        if (hEventScanCancel) {
            CloseHandle( hEventScanCancel );
            hEventScanCancel = NULL;
        }

        if (hEventScanCancelComplete) {
            CloseHandle( hEventScanCancelComplete );
            hEventScanCancelComplete = NULL;
        }
    } else {
        //
        // no UI to be shown, just do this synchronously
        //
        SfcDoScan( ScanParams );
    }

    if (ScanParams->FreeMemory) {
        MemFree( ScanParams );
    }
    return Status;
}


NTSTATUS
SfcDoForcedSxsScan(
    IN HWND hwDialogProgress,
    IN BOOL bValidate,
    IN BOOL bAllowUI
)
{
    NTSTATUS Status;

    Status = SfcLoadSxsProtection();
    if ( !NT_SUCCESS( Status ) ) {
        return Status;
    }

    ASSERT( SxsScanForcedFunc != NULL );

    if ( !SxsScanForcedFunc( hwDialogProgress, bValidate, bAllowUI ) ) {
        return STATUS_SUCCESS;
    } else
        return STATUS_NO_MEMORY;
}