2018-01-23 15:33:54 +01:00
/ *
2019-01-09 11:25:21 +01:00
* Copyright ( c ) 2019 ETH Zürich , Educational Development and Technology ( LET )
2018-01-23 15:33:54 +01:00
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
* /
2019-06-06 15:44:03 +02:00
using System.Threading ;
using SafeExamBrowser.Contracts.Communication.Events ;
using SafeExamBrowser.Contracts.Communication.Hosts ;
2018-03-15 14:32:07 +01:00
using SafeExamBrowser.Contracts.Communication.Proxies ;
2019-06-11 09:53:33 +02:00
using SafeExamBrowser.Contracts.Configuration ;
2018-01-23 15:33:54 +01:00
using SafeExamBrowser.Contracts.Configuration.Settings ;
2018-10-02 15:45:45 +02:00
using SafeExamBrowser.Contracts.Core.OperationModel ;
2018-10-03 14:35:27 +02:00
using SafeExamBrowser.Contracts.Core.OperationModel.Events ;
2018-01-24 12:34:32 +01:00
using SafeExamBrowser.Contracts.I18n ;
2018-01-23 15:33:54 +01:00
using SafeExamBrowser.Contracts.Logging ;
2019-06-11 09:53:33 +02:00
using SafeExamBrowser.Contracts.UserInterface.MessageBox ;
using SafeExamBrowser.Runtime.Operations.Events ;
2018-01-23 15:33:54 +01:00
2018-08-31 10:06:27 +02:00
namespace SafeExamBrowser.Runtime.Operations
2018-01-23 15:33:54 +01:00
{
2018-10-12 11:16:59 +02:00
internal class ServiceOperation : SessionOperation
2018-01-23 15:33:54 +01:00
{
private ILogger logger ;
2019-06-06 15:44:03 +02:00
private IRuntimeHost runtimeHost ;
2018-01-24 12:34:32 +01:00
private IServiceProxy service ;
2019-06-06 15:44:03 +02:00
private int timeout_ms ;
2018-01-23 15:33:54 +01:00
2019-06-11 09:53:33 +02:00
public override event ActionRequiredEventHandler ActionRequired ;
2018-10-12 11:16:59 +02:00
public override event StatusChangedEventHandler StatusChanged ;
2018-01-23 15:33:54 +01:00
2019-06-06 15:44:03 +02:00
public ServiceOperation (
ILogger logger ,
IRuntimeHost runtimeHost ,
IServiceProxy service ,
SessionContext sessionContext ,
int timeout_ms ) : base ( sessionContext )
2018-01-23 15:33:54 +01:00
{
this . logger = logger ;
2019-06-06 15:44:03 +02:00
this . runtimeHost = runtimeHost ;
2018-10-12 11:16:59 +02:00
this . service = service ;
2019-06-06 15:44:03 +02:00
this . timeout_ms = timeout_ms ;
2018-01-23 15:33:54 +01:00
}
2018-10-12 11:16:59 +02:00
public override OperationResult Perform ( )
2018-01-23 15:33:54 +01:00
{
2018-03-21 10:23:15 +01:00
logger . Info ( $"Initializing service session..." ) ;
2018-10-03 15:42:50 +02:00
StatusChanged ? . Invoke ( TextKey . OperationStatus_InitializeServiceSession ) ;
2018-01-23 15:33:54 +01:00
2019-06-06 15:44:03 +02:00
var success = TryEstablishConnection ( ) ;
2018-01-24 12:34:32 +01:00
2019-06-06 15:44:03 +02:00
if ( service . IsConnected )
2018-01-24 12:34:32 +01:00
{
2019-06-06 15:44:03 +02:00
success = TryStartSession ( ) ;
}
return success ? OperationResult . Success : OperationResult . Failed ;
}
public override OperationResult Repeat ( )
{
logger . Info ( $"Initializing new service session..." ) ;
StatusChanged ? . Invoke ( TextKey . OperationStatus_InitializeServiceSession ) ;
var success = false ;
if ( service . IsConnected )
{
success = TryStopSession ( ) ;
}
else
{
success = TryEstablishConnection ( ) ;
}
2018-02-28 15:49:06 +01:00
2019-06-06 15:44:03 +02:00
if ( success & & service . IsConnected )
{
success = TryStartSession ( ) ;
2018-01-24 12:34:32 +01:00
}
2018-02-28 15:49:06 +01:00
2019-06-06 15:44:03 +02:00
return success ? OperationResult . Success : OperationResult . Failed ;
}
2018-02-28 15:49:06 +01:00
2019-06-06 15:44:03 +02:00
public override OperationResult Revert ( )
{
logger . Info ( "Finalizing service session..." ) ;
StatusChanged ? . Invoke ( TextKey . OperationStatus_FinalizeServiceSession ) ;
var success = true ;
if ( service . IsConnected )
2018-03-21 10:23:15 +01:00
{
2019-06-06 15:44:03 +02:00
success & = TryStopSession ( ) ;
success & = TryTerminateConnection ( ) ;
2018-03-21 10:23:15 +01:00
}
2019-06-06 15:44:03 +02:00
return success ? OperationResult . Success : OperationResult . Failed ;
2018-01-23 15:33:54 +01:00
}
2019-06-06 15:44:03 +02:00
private bool TryEstablishConnection ( )
2018-02-01 08:37:12 +01:00
{
2019-06-06 15:44:03 +02:00
var mandatory = Context . Next . Settings . ServicePolicy = = ServicePolicy . Mandatory ;
2019-06-11 09:53:33 +02:00
var warn = Context . Next . Settings . ServicePolicy = = ServicePolicy . Warn ;
2019-06-06 15:44:03 +02:00
var connected = service . Connect ( ) ;
var success = connected | | ! mandatory ;
2018-10-10 09:19:03 +02:00
2019-06-06 15:44:03 +02:00
if ( success )
2018-03-21 10:23:15 +01:00
{
2019-06-06 15:44:03 +02:00
service . Ignore = ! connected ;
logger . Info ( $"The service is {(mandatory ? " mandatory " : " optional ")} and {(connected ? " connected . " : " not connected . All service - related operations will be ignored ! ")}" ) ;
2019-06-11 09:53:33 +02:00
if ( ! connected & & warn )
{
ActionRequired ? . Invoke ( new MessageEventArgs
{
Icon = MessageBoxIcon . Warning ,
Message = TextKey . MessageBox_ServiceUnavailableWarning ,
Title = TextKey . MessageBox_ServiceUnavailableWarningTitle
} ) ;
}
2019-06-06 15:44:03 +02:00
}
else
{
logger . Error ( "The service is mandatory but no connection could be established!" ) ;
2019-06-11 09:53:33 +02:00
ActionRequired ? . Invoke ( new MessageEventArgs
{
Icon = MessageBoxIcon . Error ,
Message = TextKey . MessageBox_ServiceUnavailableError ,
Title = TextKey . MessageBox_ServiceUnavailableErrorTitle
} ) ;
2018-03-21 10:23:15 +01:00
}
2018-02-28 15:49:06 +01:00
2019-06-06 15:44:03 +02:00
return success ;
2018-02-01 08:37:12 +01:00
}
2019-06-06 15:44:03 +02:00
private bool TryTerminateConnection ( )
2018-01-23 15:33:54 +01:00
{
2019-06-06 15:44:03 +02:00
var serviceEvent = new AutoResetEvent ( false ) ;
var serviceEventHandler = new CommunicationEventHandler ( ( ) = > serviceEvent . Set ( ) ) ;
2018-01-24 12:34:32 +01:00
2019-06-06 15:44:03 +02:00
runtimeHost . ServiceDisconnected + = serviceEventHandler ;
var success = service . Disconnect ( ) ;
if ( success )
2018-01-24 12:34:32 +01:00
{
2019-06-06 15:44:03 +02:00
logger . Info ( "Successfully disconnected from service. Waiting for service to disconnect..." ) ;
2018-03-21 10:23:15 +01:00
2019-06-06 15:44:03 +02:00
success = serviceEvent . WaitOne ( timeout_ms ) ;
2018-08-10 13:23:24 +02:00
if ( success )
2018-01-24 12:34:32 +01:00
{
2019-06-06 15:44:03 +02:00
logger . Info ( "Service disconnected successfully." ) ;
2018-01-24 12:34:32 +01:00
}
2018-08-10 13:23:24 +02:00
else
2018-01-24 12:34:32 +01:00
{
2019-06-06 15:44:03 +02:00
logger . Error ( $"Service failed to disconnect within {timeout_ms / 1000} seconds!" ) ;
2018-01-24 12:34:32 +01:00
}
}
2019-06-06 15:44:03 +02:00
else
{
logger . Error ( "Failed to disconnect from service!" ) ;
}
2018-10-10 09:19:03 +02:00
2019-06-06 15:44:03 +02:00
runtimeHost . ServiceDisconnected - = serviceEventHandler ;
return success ;
2018-01-23 15:33:54 +01:00
}
2018-02-01 08:37:12 +01:00
2019-06-06 15:44:03 +02:00
private bool TryStartSession ( )
2018-03-21 10:23:15 +01:00
{
2019-06-11 09:53:33 +02:00
var configuration = new ServiceConfiguration
{
AppConfig = Context . Next . AppConfig ,
AuthenticationToken = Context . Next . ServiceAuthenticationToken ,
SessionId = Context . Next . SessionId ,
Settings = Context . Next . Settings
} ;
2019-06-06 15:44:03 +02:00
var failure = false ;
var success = false ;
var serviceEvent = new AutoResetEvent ( false ) ;
var failureEventHandler = new CommunicationEventHandler ( ( ) = > { failure = true ; serviceEvent . Set ( ) ; } ) ;
var successEventHandler = new CommunicationEventHandler ( ( ) = > { success = true ; serviceEvent . Set ( ) ; } ) ;
runtimeHost . ServiceFailed + = failureEventHandler ;
runtimeHost . ServiceSessionStarted + = successEventHandler ;
logger . Info ( "Starting new service session..." ) ;
2019-06-11 09:53:33 +02:00
var communication = service . StartSession ( configuration ) ;
2019-06-06 15:44:03 +02:00
if ( communication . Success )
{
serviceEvent . WaitOne ( timeout_ms ) ;
if ( success )
{
logger . Info ( "Successfully started new service session." ) ;
}
else if ( failure )
{
logger . Error ( "An error occurred while attempting to start a new service session! Please check the service log for further information." ) ;
}
else
{
logger . Error ( $"Failed to start new service session within {timeout_ms / 1000} seconds!" ) ;
}
}
else
{
logger . Error ( "Failed to communicate session start to service!" ) ;
}
runtimeHost . ServiceFailed - = failureEventHandler ;
runtimeHost . ServiceSessionStarted - = successEventHandler ;
return success ;
2018-03-21 10:23:15 +01:00
}
2019-06-06 15:44:03 +02:00
private bool TryStopSession ( )
2018-03-21 10:23:15 +01:00
{
2019-06-06 15:44:03 +02:00
var failure = false ;
var success = false ;
var serviceEvent = new AutoResetEvent ( false ) ;
var failureEventHandler = new CommunicationEventHandler ( ( ) = > { failure = true ; serviceEvent . Set ( ) ; } ) ;
var successEventHandler = new CommunicationEventHandler ( ( ) = > { success = true ; serviceEvent . Set ( ) ; } ) ;
runtimeHost . ServiceFailed + = failureEventHandler ;
runtimeHost . ServiceSessionStopped + = successEventHandler ;
logger . Info ( "Stopping current service session..." ) ;
2019-06-11 09:53:33 +02:00
var communication = service . StopSession ( Context . Current . SessionId ) ;
2019-06-06 15:44:03 +02:00
if ( communication . Success )
{
serviceEvent . WaitOne ( timeout_ms ) ;
if ( success )
{
logger . Info ( "Successfully stopped service session." ) ;
}
else if ( failure )
{
logger . Error ( "An error occurred while attempting to stop the current service session! Please check the service log for further information." ) ;
}
else
{
logger . Error ( $"Failed to stop service session within {timeout_ms / 1000} seconds!" ) ;
}
}
else
{
logger . Error ( "Failed to communicate session stop to service!" ) ;
}
runtimeHost . ServiceFailed - = failureEventHandler ;
runtimeHost . ServiceSessionStopped - = successEventHandler ;
return success ;
2018-03-21 10:23:15 +01:00
}
2018-01-23 15:33:54 +01:00
}
}