/********************************************************************/
/*	      Copyright(c)  1995 Microsoft Corporation		            */
/********************************************************************/

//***
//
// Filename:	evdsptch.c
//
// Description: This module contains the event dispatcher for the
//		        DDM's procedure-driven state machine
//
// Author:	    Stefan Solomon (stefans)    June 9, 1992.
//
//***
#include "ddm.h"
#include "handlers.h"
#include "objects.h"
#include "timer.h"
#include "util.h"
#include <raserror.h>
#include <ddmif.h>
#include <sechost.h>
#include <stdlib.h>
#include "rasmanif.h"

//***
//
// Function:	EventDispatcher
//
// Descr:	    Waits for events to be signaled and invokes the proper
//		        event handler. Returns when DDM has terminated.
//
//***
DWORD
EventDispatcher(
    IN LPVOID arg
)
{
    EVENT_HANDLER * pEventHandler;
    DWORD           dwSignaledEvent;

    //
    // Indicate that this thread is running
    //

    InterlockedIncrement( gblDDMConfigInfo.lpdwNumThreadsRunning );

    RegNotifyChangeKeyValue( gblDDMConfigInfo.hkeyParameters,
                             TRUE,
                             REG_NOTIFY_CHANGE_LAST_SET,
                             gblSupervisorEvents[DDM_EVENT_CHANGE_NOTIFICATION],
                             TRUE );
    RegNotifyChangeKeyValue( gblDDMConfigInfo.hkeyAccounting,
                             TRUE,
                             REG_NOTIFY_CHANGE_LAST_SET,
                             gblSupervisorEvents[DDM_EVENT_CHANGE_NOTIFICATION1],
                             TRUE );

    RegNotifyChangeKeyValue( gblDDMConfigInfo.hkeyAuthentication,
                             TRUE,
                             REG_NOTIFY_CHANGE_LAST_SET,
                             gblSupervisorEvents[DDM_EVENT_CHANGE_NOTIFICATION2],
                             TRUE );
    while( TRUE )
    {
        dwSignaledEvent = WaitForMultipleObjectsEx( 
                            NUM_DDM_EVENTS
                            + ( gblDeviceTable.NumDeviceBuckets * 3 ),
                            gblSupervisorEvents,
                            FALSE, 
                            INFINITE,
                            TRUE);

        if ( ( dwSignaledEvent == 0xFFFFFFFF ) || 
             ( dwSignaledEvent == WAIT_TIMEOUT ) )
        {
            DDMTRACE2("WaitForMultipleObjectsEx returned %d, GetLastError=%d",
                       dwSignaledEvent, GetLastError() );

            continue;
        }

        //
        // DDM has terminated so return
        //

        if ( dwSignaledEvent == DDM_EVENT_SVC_TERMINATED )
        {
            LPDWORD lpdwNumThreadsRunning = 
                                    gblDDMConfigInfo.lpdwNumThreadsRunning;

            //
            // If we were running and we are now shutting down, clean up
            // gracefully
            //

            if ( gblDDMConfigInfo.pServiceStatus->dwCurrentState 
                                                    == SERVICE_STOP_PENDING )
            {
                DDMCleanUp();
            }

            //
            // Decrease the count for this thread
            //

            InterlockedDecrement( lpdwNumThreadsRunning );

            return( NO_ERROR );
        }

        //
        // invoke the handler associated with the signaled event
        //

        if ( dwSignaledEvent < NUM_DDM_EVENTS ) 
        {
            //
            // Some DDM event
            //

            gblEventHandlerTable[dwSignaledEvent].EventHandler();
        }
        else if ( dwSignaledEvent < ( NUM_DDM_EVENTS 
                                     + gblDeviceTable.NumDeviceBuckets ) )
        {
            //
            // The event was a RASMAN event
            //

            RmEventHandler( dwSignaledEvent );
        }
        else if ( dwSignaledEvent < ( NUM_DDM_EVENTS 
                                      + gblDeviceTable.NumDeviceBuckets * 2 ) )
        {
            //
            // A frame was received on a port
            //

            RmRecvFrameEventHandler( dwSignaledEvent );
        }
        else if ( dwSignaledEvent != WAIT_IO_COMPLETION )
        {
            //
            // We got a disconnect on a dialed out port
            //

            RasApiDisconnectHandler( dwSignaledEvent );
        }
    }

    return( NO_ERROR );
}

//***
//
//  Function:	SecurityDllEventHandler
//
//  Descr:	    This will handle all events from the 3rd party security Dll.
//		        Either the security dialog with the client completed 
//              successfully, in which case we continue on with the connection,
//              or we log the error and bring down the line.
//
//***

VOID 
SecurityDllEventHandler(
    VOID
)
{
    LPWSTR              auditstrp[3];
    SECURITY_MESSAGE    message;
    PDEVICE_OBJECT      pDevObj;
    DWORD               dwBucketIndex;
    WCHAR               wchUserName[UNLEN+1];

    //
    // loop to get all messages
    //

    while( ServerReceiveMessage( MESSAGEQ_ID_SECURITY, (BYTE *) &message ) )
    {

        EnterCriticalSection( &(gblDeviceTable.CriticalSection) );

        //
	    // identify the message recipient
        //

        if ( ( pDevObj = DeviceObjGetPointer( message.hPort ) ) == NULL )
        {

            LeaveCriticalSection( &(gblDeviceTable.CriticalSection) );

	        return;
	    }

        //
        // action on the message type
        //

        switch( message.dwMsgId )
        {

        case SECURITYMSG_SUCCESS:

            //
            // Stop timer for 3rd party security
            //

            TimerQRemove( (HANDLE)pDevObj->hPort, SvSecurityTimeout );

            RasFreeBuffer( pDevObj->pRasmanSendBuffer );

            pDevObj->pRasmanSendBuffer = NULL;

            //
            // copy the user name
            //

            MultiByteToWideChar( CP_ACP,
                                 0,
                                 message.UserName, 
                                -1,
                                 pDevObj->wchUserName, 
                                 UNLEN+1 );

            //
            // copy the domain name
            //

            MultiByteToWideChar( CP_ACP,
                                 0,
                                 message.Domain, 
                                 -1,
                                 pDevObj->wchDomainName, 
                                 DNLEN+1 );

            pDevObj->SecurityState = DEV_OBJ_SECURITY_DIALOG_INACTIVE;

            //
            // Change RASMAN state to CONNECTED from LISTENCOMPLETE and signal
            // RmEventHandler
            //

	        RasPortConnectComplete(pDevObj->hPort);

            dwBucketIndex = DeviceObjHashPortToBucket( pDevObj->hPort );

            SetEvent( gblSupervisorEvents[NUM_DDM_EVENTS+dwBucketIndex] );

            DDM_PRINT(
                   gblDDMConfigInfo.dwTraceId,
                   TRACE_FSM,
	               "SecurityDllEventHandler: Security DLL success \n" );

            break;

        case SECURITYMSG_FAILURE:

            //
            // Log the fact that the use failed to pass 3rd party security.
            //

            MultiByteToWideChar( CP_ACP,
                                 0,
                                 message.UserName, 
                                 -1,
                                 wchUserName, 
                                 UNLEN+1 );

            auditstrp[0] = wchUserName;
            auditstrp[1] = pDevObj->wchPortName;

            DDMLogError( ROUTERLOG_SEC_AUTH_FAILURE, 2, auditstrp, NO_ERROR );

            //
            // Hang up the line
            //

            DDM_PRINT(
                   gblDDMConfigInfo.dwTraceId,
                   TRACE_FSM,
	               "SecurityDllEventHandler:Security DLL failure %s\n",
                    message.UserName );

            if ( pDevObj->SecurityState == DEV_OBJ_SECURITY_DIALOG_ACTIVE )
            {
                DevStartClosing(pDevObj);
            }
            else if ( pDevObj->SecurityState==DEV_OBJ_SECURITY_DIALOG_STOPPING )
            {
                pDevObj->SecurityState = DEV_OBJ_SECURITY_DIALOG_INACTIVE;

                DevCloseComplete(pDevObj);
            }

            break;

        case SECURITYMSG_ERROR:

            auditstrp[0] = pDevObj->wchPortName;

	        DDMLogErrorString( ROUTERLOG_SEC_AUTH_INTERNAL_ERROR, 1, auditstrp, 
                               message.dwError, 1);

            DDM_PRINT(
                   gblDDMConfigInfo.dwTraceId,
                   TRACE_FSM,
                   "SecurityDllEventHandler:Security DLL failure %x\n",
                    message.dwError );

            if ( pDevObj->SecurityState == DEV_OBJ_SECURITY_DIALOG_ACTIVE )
            {
                DevStartClosing(pDevObj);
            }
            else if ( pDevObj->SecurityState==DEV_OBJ_SECURITY_DIALOG_STOPPING )
            {
                pDevObj->SecurityState = DEV_OBJ_SECURITY_DIALOG_INACTIVE;

                DevCloseComplete(pDevObj);
            }

            break;

        default:

	        RTASSERT(FALSE);
	        break;
        }

        LeaveCriticalSection( &(gblDeviceTable.CriticalSection) );
    }
}