2017-07-26 14:36:20 +02:00
/ *
2024-03-05 18:37:42 +01:00
* Copyright ( c ) 2024 ETH Zürich , IT Services
2017-07-26 14:36:20 +02: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/.
* /
2017-07-27 13:57:12 +02:00
using System ;
2019-10-09 14:04:27 +02:00
using System.Collections.Generic ;
2018-06-21 07:56:25 +02:00
using System.IO ;
2019-10-04 16:36:12 +02:00
using System.Linq ;
2020-09-10 12:35:58 +02:00
using System.Text.RegularExpressions ;
2020-07-29 23:39:05 +02:00
using System.Threading ;
2022-11-24 14:50:25 +01:00
using System.Threading.Tasks ;
2019-11-05 10:08:19 +01:00
using SafeExamBrowser.Applications.Contracts ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.Browser.Contracts ;
2019-09-06 08:13:27 +02:00
using SafeExamBrowser.Browser.Contracts.Events ;
2019-10-04 16:36:12 +02:00
using SafeExamBrowser.Client.Operations.Events ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.Communication.Contracts.Data ;
using SafeExamBrowser.Communication.Contracts.Events ;
using SafeExamBrowser.Communication.Contracts.Hosts ;
using SafeExamBrowser.Communication.Contracts.Proxies ;
using SafeExamBrowser.Configuration.Contracts.Cryptography ;
2022-07-18 21:37:04 +02:00
using SafeExamBrowser.Configuration.Contracts.Integrity ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.Core.Contracts.OperationModel ;
using SafeExamBrowser.Core.Contracts.OperationModel.Events ;
using SafeExamBrowser.I18n.Contracts ;
using SafeExamBrowser.Logging.Contracts ;
2019-10-01 11:30:53 +02:00
using SafeExamBrowser.Monitoring.Contracts.Applications ;
2019-09-05 09:00:41 +02:00
using SafeExamBrowser.Monitoring.Contracts.Display ;
2020-06-29 19:29:48 +02:00
using SafeExamBrowser.Monitoring.Contracts.System ;
2024-02-29 21:05:43 +01:00
using SafeExamBrowser.Proctoring.Contracts ;
using SafeExamBrowser.Proctoring.Contracts.Events ;
2020-07-28 19:56:25 +02:00
using SafeExamBrowser.Server.Contracts ;
2020-07-31 19:57:08 +02:00
using SafeExamBrowser.Server.Contracts.Data ;
2019-10-01 11:30:53 +02:00
using SafeExamBrowser.Settings ;
2024-05-02 10:30:26 +02:00
using SafeExamBrowser.SystemComponents.Contracts.Network ;
using SafeExamBrowser.SystemComponents.Contracts.Network.Events ;
2022-12-21 05:37:03 +01:00
using SafeExamBrowser.SystemComponents.Contracts.Registry ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.UserInterface.Contracts ;
2020-01-22 16:08:57 +01:00
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.UserInterface.Contracts.MessageBox ;
using SafeExamBrowser.UserInterface.Contracts.Shell ;
using SafeExamBrowser.UserInterface.Contracts.Windows ;
2019-10-11 15:46:15 +02:00
using SafeExamBrowser.UserInterface.Contracts.Windows.Data ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.WindowsApi.Contracts ;
2017-07-26 14:36:20 +02:00
2018-08-31 10:06:27 +02:00
namespace SafeExamBrowser.Client
2017-07-26 14:36:20 +02:00
{
2020-03-16 13:38:25 +01:00
internal class ClientController
2017-07-26 14:36:20 +02:00
{
2022-07-18 21:37:04 +02:00
private readonly IActionCenter actionCenter ;
private readonly IApplicationMonitor applicationMonitor ;
private readonly ClientContext context ;
private readonly IDisplayMonitor displayMonitor ;
private readonly IExplorerShell explorerShell ;
private readonly IFileSystemDialog fileSystemDialog ;
private readonly IHashAlgorithm hashAlgorithm ;
private readonly ILogger logger ;
private readonly IMessageBox messageBox ;
2024-05-02 10:30:26 +02:00
private readonly INetworkAdapter networkAdapter ;
2022-07-18 21:37:04 +02:00
private readonly IOperationSequence operations ;
2022-12-21 05:37:03 +01:00
private readonly IRegistry registry ;
2022-07-18 21:37:04 +02:00
private readonly IRuntimeProxy runtime ;
private readonly Action shutdown ;
private readonly ISplashScreen splashScreen ;
private readonly ISystemMonitor systemMonitor ;
private readonly ITaskbar taskbar ;
private readonly IText text ;
private readonly IUserInterfaceFactory uiFactory ;
2017-07-26 14:36:20 +02:00
2019-10-01 16:24:10 +02:00
private IBrowserApplication Browser = > context . Browser ;
private IClientHost ClientHost = > context . ClientHost ;
2022-11-24 14:50:25 +01:00
private IIntegrityModule IntegrityModule = > context . IntegrityModule ;
2024-02-29 21:05:43 +01:00
private IProctoringController Proctoring = > context . Proctoring ;
2020-07-28 19:56:25 +02:00
private IServerProxy Server = > context . Server ;
2019-10-01 16:24:10 +02:00
private AppSettings Settings = > context . Settings ;
2018-02-20 15:15:26 +01:00
2022-08-31 14:11:19 +02:00
private ILockScreen lockScreen ;
2022-07-18 21:37:04 +02:00
private bool sessionLocked ;
2020-03-16 13:38:25 +01:00
internal ClientController (
2019-03-06 16:10:00 +01:00
IActionCenter actionCenter ,
2019-10-01 11:30:53 +02:00
IApplicationMonitor applicationMonitor ,
2019-10-01 16:24:10 +02:00
ClientContext context ,
2017-08-11 08:28:17 +02:00
IDisplayMonitor displayMonitor ,
2018-08-17 14:48:50 +02:00
IExplorerShell explorerShell ,
2020-01-22 16:08:57 +01:00
IFileSystemDialog fileSystemDialog ,
2019-01-10 10:04:30 +01:00
IHashAlgorithm hashAlgorithm ,
2017-07-27 11:46:31 +02:00
ILogger logger ,
2018-03-14 12:07:20 +01:00
IMessageBox messageBox ,
2024-05-02 10:30:26 +02:00
INetworkAdapter networkAdapter ,
2018-02-12 12:21:55 +01:00
IOperationSequence operations ,
2022-12-21 05:37:03 +01:00
IRegistry registry ,
2018-02-14 15:26:05 +01:00
IRuntimeProxy runtime ,
2018-02-21 14:01:21 +01:00
Action shutdown ,
2020-02-14 14:43:08 +01:00
ISplashScreen splashScreen ,
2020-06-29 19:29:48 +02:00
ISystemMonitor systemMonitor ,
2017-07-27 11:46:31 +02:00
ITaskbar taskbar ,
2018-07-04 09:53:33 +02:00
IText text ,
2019-10-01 11:30:53 +02:00
IUserInterfaceFactory uiFactory )
2017-07-26 14:36:20 +02:00
{
2019-03-06 16:10:00 +01:00
this . actionCenter = actionCenter ;
2019-10-01 11:30:53 +02:00
this . applicationMonitor = applicationMonitor ;
2019-10-01 16:24:10 +02:00
this . context = context ;
2017-08-11 08:28:17 +02:00
this . displayMonitor = displayMonitor ;
2018-08-17 14:48:50 +02:00
this . explorerShell = explorerShell ;
2020-01-22 16:08:57 +01:00
this . fileSystemDialog = fileSystemDialog ;
2019-01-10 10:04:30 +01:00
this . hashAlgorithm = hashAlgorithm ;
2017-07-26 14:36:20 +02:00
this . logger = logger ;
2018-03-14 12:07:20 +01:00
this . messageBox = messageBox ;
2024-05-02 10:30:26 +02:00
this . networkAdapter = networkAdapter ;
2018-02-12 12:21:55 +01:00
this . operations = operations ;
2022-12-21 05:37:03 +01:00
this . registry = registry ;
2018-02-14 15:26:05 +01:00
this . runtime = runtime ;
2018-02-21 14:01:21 +01:00
this . shutdown = shutdown ;
2020-02-14 14:43:08 +01:00
this . splashScreen = splashScreen ;
2020-06-29 19:29:48 +02:00
this . systemMonitor = systemMonitor ;
2017-07-26 14:36:20 +02:00
this . taskbar = taskbar ;
2018-07-04 09:53:33 +02:00
this . text = text ;
2018-02-21 14:01:21 +01:00
this . uiFactory = uiFactory ;
2017-07-26 14:36:20 +02:00
}
2020-03-16 13:38:25 +01:00
internal bool TryStart ( )
2018-02-12 12:21:55 +01:00
{
2018-08-16 11:23:37 +02:00
logger . Info ( "Initiating startup procedure..." ) ;
2018-02-22 10:00:18 +01:00
2019-10-01 11:30:53 +02:00
operations . ActionRequired + = Operations_ActionRequired ;
2018-10-03 14:35:27 +02:00
operations . ProgressChanged + = Operations_ProgressChanged ;
operations . StatusChanged + = Operations_StatusChanged ;
2018-02-21 14:01:21 +01:00
2020-02-14 14:43:08 +01:00
splashScreen . Show ( ) ;
splashScreen . BringToForeground ( ) ;
2018-02-28 15:49:06 +01:00
var success = operations . TryPerform ( ) = = OperationResult . Success ;
2018-02-14 15:26:05 +01:00
2018-02-20 15:15:26 +01:00
if ( success )
{
RegisterEvents ( ) ;
2019-03-06 16:10:00 +01:00
ShowShell ( ) ;
2019-11-20 15:30:53 +01:00
AutoStartApplications ( ) ;
2022-07-18 21:37:04 +02:00
ScheduleIntegrityVerification ( ) ;
2018-02-27 15:28:54 +01:00
2018-08-10 13:23:24 +02:00
var communication = runtime . InformClientReady ( ) ;
if ( communication . Success )
2018-02-27 15:28:54 +01:00
{
2018-08-16 11:23:37 +02:00
logger . Info ( "Application successfully initialized." ) ;
2018-08-10 13:23:24 +02:00
logger . Log ( string . Empty ) ;
2022-11-24 14:50:25 +01:00
VerifySessionIntegrity ( ) ;
2018-02-27 15:28:54 +01:00
}
2018-08-10 13:23:24 +02:00
else
2018-02-27 15:28:54 +01:00
{
2018-08-10 13:23:24 +02:00
success = false ;
logger . Error ( "Failed to inform runtime that client is ready!" ) ;
2018-02-27 15:28:54 +01:00
}
2018-02-22 10:00:18 +01:00
}
else
{
2018-08-16 11:23:37 +02:00
logger . Info ( "Application startup aborted!" ) ;
2018-02-22 10:00:18 +01:00
logger . Log ( string . Empty ) ;
2018-02-20 15:15:26 +01:00
}
2020-02-14 14:43:08 +01:00
splashScreen . Hide ( ) ;
2019-03-06 16:10:00 +01:00
2018-02-20 15:15:26 +01:00
return success ;
2018-02-12 12:21:55 +01:00
}
2020-03-16 13:38:25 +01:00
internal void Terminate ( )
2017-07-26 14:36:20 +02:00
{
2018-02-22 10:00:18 +01:00
logger . Log ( string . Empty ) ;
2018-08-16 11:23:37 +02:00
logger . Info ( "Initiating shutdown procedure..." ) ;
2018-02-22 10:00:18 +01:00
2020-02-14 14:43:08 +01:00
splashScreen . Show ( ) ;
splashScreen . BringToForeground ( ) ;
2017-07-26 14:36:20 +02:00
2019-11-14 14:03:43 +01:00
CloseShell ( ) ;
2018-02-21 14:01:21 +01:00
DeregisterEvents ( ) ;
2022-11-24 14:50:25 +01:00
UpdateSessionIntegrity ( ) ;
2022-12-21 05:37:03 +01:00
TerminateIntegrityVerification ( ) ;
2018-02-27 15:28:54 +01:00
2018-10-10 09:19:03 +02:00
var success = operations . TryRevert ( ) = = OperationResult . Success ;
2018-02-22 10:00:18 +01:00
if ( success )
{
2018-08-16 11:23:37 +02:00
logger . Info ( "Application successfully finalized." ) ;
2018-02-22 10:00:18 +01:00
logger . Log ( string . Empty ) ;
}
else
{
2018-08-16 11:23:37 +02:00
logger . Info ( "Shutdown procedure failed!" ) ;
2018-02-22 10:00:18 +01:00
logger . Log ( string . Empty ) ;
}
2018-02-21 14:01:21 +01:00
2018-10-04 11:24:16 +02:00
splashScreen . Close ( ) ;
2018-02-20 15:15:26 +01:00
}
2018-02-14 15:26:05 +01:00
2020-03-16 13:38:25 +01:00
internal void UpdateAppConfig ( )
2019-10-01 16:24:10 +02:00
{
2020-02-14 14:43:08 +01:00
splashScreen . AppConfig = context . AppConfig ;
2019-10-01 16:24:10 +02:00
}
2018-02-20 15:15:26 +01:00
private void RegisterEvents ( )
{
2019-03-15 11:38:59 +01:00
actionCenter . QuitButtonClicked + = Shell_QuitButtonClicked ;
2019-10-01 11:30:53 +02:00
applicationMonitor . ExplorerStarted + = ApplicationMonitor_ExplorerStarted ;
2019-10-09 14:04:27 +02:00
applicationMonitor . TerminationFailed + = ApplicationMonitor_TerminationFailed ;
2018-06-21 07:56:25 +02:00
Browser . ConfigurationDownloadRequested + = Browser_ConfigurationDownloadRequested ;
2022-05-02 16:51:18 +02:00
Browser . LoseFocusRequested + = Browser_LoseFocusRequested ;
2023-11-01 13:52:39 +01:00
Browser . TerminationRequested + = Browser_TerminationRequested ;
Browser . UserIdentifierDetected + = Browser_UserIdentifierDetected ;
2020-07-31 19:57:08 +02:00
ClientHost . ExamSelectionRequested + = ClientHost_ExamSelectionRequested ;
2018-12-14 12:31:31 +01:00
ClientHost . MessageBoxRequested + = ClientHost_MessageBoxRequested ;
2018-07-04 09:53:33 +02:00
ClientHost . PasswordRequested + = ClientHost_PasswordRequested ;
2020-02-10 16:47:50 +01:00
ClientHost . ReconfigurationAborted + = ClientHost_ReconfigurationAborted ;
2018-07-04 09:53:33 +02:00
ClientHost . ReconfigurationDenied + = ClientHost_ReconfigurationDenied ;
2020-07-31 20:35:18 +02:00
ClientHost . ServerFailureActionRequested + = ClientHost_ServerFailureActionRequested ;
2018-02-20 15:15:26 +01:00
ClientHost . Shutdown + = ClientHost_Shutdown ;
displayMonitor . DisplayChanged + = DisplayMonitor_DisplaySettingsChanged ;
2024-05-02 10:30:26 +02:00
networkAdapter . CredentialsRequired + = NetworkAdapter_CredentialsRequired ;
2022-12-21 05:37:03 +01:00
registry . ValueChanged + = Registry_ValueChanged ;
2018-02-27 15:28:54 +01:00
runtime . ConnectionLost + = Runtime_ConnectionLost ;
2024-01-11 19:01:56 +01:00
systemMonitor . SessionChanged + = SystemMonitor_SessionChanged ;
2021-11-24 08:42:07 +01:00
taskbar . LoseFocusRequested + = Taskbar_LoseFocusRequested ;
2019-03-15 11:38:59 +01:00
taskbar . QuitButtonClicked + = Shell_QuitButtonClicked ;
2019-11-14 14:03:43 +01:00
foreach ( var activator in context . Activators . OfType < ITerminationActivator > ( ) )
{
activator . Activated + = TerminationActivator_Activated ;
}
2020-07-28 19:56:25 +02:00
if ( Server ! = null )
{
2022-08-31 14:11:19 +02:00
Server . LockScreenConfirmed + = Server_LockScreenConfirmed ;
Server . LockScreenRequested + = Server_LockScreenRequested ;
2022-09-02 14:56:49 +02:00
Server . TerminationRequested + = Server_TerminationRequested ;
2020-07-28 19:56:25 +02:00
}
2018-02-20 15:15:26 +01:00
}
2018-02-14 15:26:05 +01:00
2018-02-20 15:15:26 +01:00
private void DeregisterEvents ( )
{
2019-03-15 11:38:59 +01:00
actionCenter . QuitButtonClicked - = Shell_QuitButtonClicked ;
2019-10-01 11:30:53 +02:00
applicationMonitor . ExplorerStarted - = ApplicationMonitor_ExplorerStarted ;
2019-10-09 14:04:27 +02:00
applicationMonitor . TerminationFailed - = ApplicationMonitor_TerminationFailed ;
2018-02-20 15:15:26 +01:00
displayMonitor . DisplayChanged - = DisplayMonitor_DisplaySettingsChanged ;
2022-12-21 05:37:03 +01:00
registry . ValueChanged - = Registry_ValueChanged ;
2018-02-27 15:28:54 +01:00
runtime . ConnectionLost - = Runtime_ConnectionLost ;
2024-01-11 19:01:56 +01:00
systemMonitor . SessionChanged - = SystemMonitor_SessionChanged ;
2024-02-29 21:05:43 +01:00
taskbar . LoseFocusRequested - = Taskbar_LoseFocusRequested ;
2019-03-15 11:38:59 +01:00
taskbar . QuitButtonClicked - = Shell_QuitButtonClicked ;
2019-01-23 10:07:20 +01:00
if ( Browser ! = null )
{
Browser . ConfigurationDownloadRequested - = Browser_ConfigurationDownloadRequested ;
2022-05-02 16:51:18 +02:00
Browser . LoseFocusRequested - = Browser_LoseFocusRequested ;
2023-11-01 13:52:39 +01:00
Browser . TerminationRequested - = Browser_TerminationRequested ;
Browser . UserIdentifierDetected - = Browser_UserIdentifierDetected ;
2019-01-23 10:07:20 +01:00
}
if ( ClientHost ! = null )
{
2020-07-31 19:57:08 +02:00
ClientHost . ExamSelectionRequested - = ClientHost_ExamSelectionRequested ;
2019-01-23 10:07:20 +01:00
ClientHost . MessageBoxRequested - = ClientHost_MessageBoxRequested ;
ClientHost . PasswordRequested - = ClientHost_PasswordRequested ;
2020-02-10 16:47:50 +01:00
ClientHost . ReconfigurationAborted - = ClientHost_ReconfigurationAborted ;
2019-01-23 10:07:20 +01:00
ClientHost . ReconfigurationDenied - = ClientHost_ReconfigurationDenied ;
2020-07-31 20:35:18 +02:00
ClientHost . ServerFailureActionRequested - = ClientHost_ServerFailureActionRequested ;
2019-01-23 10:07:20 +01:00
ClientHost . Shutdown - = ClientHost_Shutdown ;
}
2019-11-14 14:03:43 +01:00
2020-07-28 19:56:25 +02:00
if ( Server ! = null )
{
2022-08-31 14:11:19 +02:00
Server . LockScreenConfirmed - = Server_LockScreenConfirmed ;
Server . LockScreenRequested - = Server_LockScreenRequested ;
2022-09-02 14:56:49 +02:00
Server . TerminationRequested - = Server_TerminationRequested ;
2020-07-28 19:56:25 +02:00
}
2019-11-14 14:03:43 +01:00
foreach ( var activator in context . Activators . OfType < ITerminationActivator > ( ) )
{
activator . Activated - = TerminationActivator_Activated ;
}
}
private void CloseShell ( )
{
2020-08-06 14:06:29 +02:00
if ( Settings ? . ActionCenter . EnableActionCenter = = true )
2019-11-14 14:03:43 +01:00
{
actionCenter . Close ( ) ;
}
2020-08-06 14:06:29 +02:00
if ( Settings ? . Taskbar . EnableTaskbar = = true )
2019-11-14 14:03:43 +01:00
{
taskbar . Close ( ) ;
}
2017-07-26 14:36:20 +02:00
}
2019-03-06 16:10:00 +01:00
private void ShowShell ( )
{
2019-11-14 14:03:43 +01:00
if ( Settings . ActionCenter . EnableActionCenter )
{
2020-04-01 13:49:32 +02:00
actionCenter . Promote ( ) ;
2019-11-14 14:03:43 +01:00
}
2019-03-06 16:10:00 +01:00
if ( Settings . Taskbar . EnableTaskbar )
{
taskbar . Show ( ) ;
}
}
2019-11-20 15:30:53 +01:00
private void AutoStartApplications ( )
2018-11-15 08:45:17 +01:00
{
2020-01-10 10:25:51 +01:00
if ( Settings . Browser . EnableBrowser & & Browser . AutoStart )
2019-12-02 15:48:06 +01:00
{
logger . Info ( "Auto-starting browser..." ) ;
Browser . Start ( ) ;
}
2019-11-20 15:30:53 +01:00
foreach ( var application in context . Applications )
{
2019-12-02 15:48:06 +01:00
if ( application . AutoStart )
2019-11-20 15:30:53 +01:00
{
2019-12-02 15:48:06 +01:00
logger . Info ( $"Auto-starting '{application.Name}'..." ) ;
2019-11-20 15:30:53 +01:00
application . Start ( ) ;
}
}
2018-11-15 08:45:17 +01:00
}
2024-02-29 21:05:43 +01:00
private void PrepareShutdown ( )
{
FinalizeProctoring ( ) ;
}
private void FinalizeProctoring ( )
{
if ( Proctoring ! = default & & Proctoring . HasRemainingWork ( ) )
{
var dialog = uiFactory . CreateProctoringFinalizationDialog ( ) ;
var handler = new RemainingWorkUpdatedEventHandler ( ( args ) = > dialog . Update ( args ) ) ;
Task . Run ( ( ) = >
{
Proctoring . RemainingWorkUpdated + = handler ;
Proctoring . ExecuteRemainingWork ( ) ;
Proctoring . RemainingWorkUpdated - = handler ;
} ) ;
dialog . Show ( ) ;
}
}
2022-07-18 21:37:04 +02:00
private void ScheduleIntegrityVerification ( )
{
const int FIVE_MINUTES = 300000 ;
const int TEN_MINUTES = 600000 ;
var timer = new System . Timers . Timer ( ) ;
timer . AutoReset = false ;
2023-09-01 12:28:03 +02:00
timer . Elapsed + = ( o , args ) = > VerifyApplicationIntegrity ( ) ;
timer . Interval = TEN_MINUTES + ( new Random ( ) . NextDouble ( ) * FIVE_MINUTES ) ;
timer . Start ( ) ;
2024-03-05 16:39:21 +01:00
if ( Settings . Security . VerifyCursorConfiguration )
2023-09-01 12:28:03 +02:00
{
2024-03-05 16:39:21 +01:00
if ( registry . TryGetNames ( RegistryValue . UserHive . Cursors_Key , out var names ) )
2023-09-01 12:28:03 +02:00
{
2024-03-05 16:39:21 +01:00
foreach ( var name in names )
{
registry . StartMonitoring ( RegistryValue . UserHive . Cursors_Key , name ) ;
}
}
else
{
logger . Warn ( "Failed to start monitoring cursor registry values!" ) ;
2023-09-01 12:28:03 +02:00
}
}
if ( Settings . Service . IgnoreService )
{
registry . StartMonitoring ( RegistryValue . MachineHive . EaseOfAccess_Key , RegistryValue . MachineHive . EaseOfAccess_Name ) ;
}
}
private void VerifyApplicationIntegrity ( )
{
logger . Info ( $"Attempting to verify application integrity..." ) ;
2022-07-18 21:37:04 +02:00
2023-09-01 12:28:03 +02:00
if ( IntegrityModule . TryVerifyCodeSignature ( out var isValid ) )
{
if ( isValid )
2022-07-18 21:37:04 +02:00
{
2023-09-01 12:28:03 +02:00
logger . Info ( "Application integrity successfully verified." ) ;
2022-07-18 21:37:04 +02:00
}
else
{
2023-09-01 12:28:03 +02:00
logger . Warn ( "Application integrity is compromised!" ) ;
ShowLockScreen ( text . Get ( TextKey . LockScreen_ApplicationIntegrityMessage ) , text . Get ( TextKey . LockScreen_Title ) , Enumerable . Empty < LockScreenOption > ( ) ) ;
2022-07-18 21:37:04 +02:00
}
2023-09-01 12:28:03 +02:00
}
else
2022-12-22 17:21:11 +01:00
{
2023-09-01 12:28:03 +02:00
logger . Warn ( "Failed to verify application integrity!" ) ;
2022-12-22 17:21:11 +01:00
}
2022-07-18 21:37:04 +02:00
}
2022-11-24 14:50:25 +01:00
private void VerifySessionIntegrity ( )
{
var hasQuitPassword = ! string . IsNullOrEmpty ( Settings . Security . QuitPasswordHash ) ;
if ( hasQuitPassword )
{
logger . Info ( $"Attempting to verify session integrity..." ) ;
if ( IntegrityModule . TryVerifySessionIntegrity ( Settings . Browser . ConfigurationKey , Settings . Browser . StartUrl , out var isValid ) )
{
if ( isValid )
{
logger . Info ( "Session integrity successfully verified." ) ;
IntegrityModule . CacheSession ( Settings . Browser . ConfigurationKey , Settings . Browser . StartUrl ) ;
}
else
{
logger . Warn ( "Session integrity is compromised!" ) ;
Task . Delay ( 1000 ) . ContinueWith ( _ = >
{
ShowLockScreen ( text . Get ( TextKey . LockScreen_SessionIntegrityMessage ) , text . Get ( TextKey . LockScreen_Title ) , Enumerable . Empty < LockScreenOption > ( ) ) ;
} ) ;
}
}
else
{
logger . Warn ( "Failed to verify session integrity!" ) ;
}
}
}
private void UpdateSessionIntegrity ( )
{
var hasQuitPassword = ! string . IsNullOrEmpty ( Settings ? . Security . QuitPasswordHash ) ;
if ( hasQuitPassword )
{
IntegrityModule ? . ClearSession ( Settings . Browser . ConfigurationKey , Settings . Browser . StartUrl ) ;
}
}
2022-12-21 05:37:03 +01:00
private void TerminateIntegrityVerification ( )
{
registry . StopMonitoring ( ) ;
}
2019-10-01 11:30:53 +02:00
private void ApplicationMonitor_ExplorerStarted ( )
{
logger . Info ( "Trying to terminate Windows explorer..." ) ;
explorerShell . Terminate ( ) ;
2020-02-21 09:38:39 +01:00
logger . Info ( "Re-initializing working area..." ) ;
displayMonitor . InitializePrimaryDisplay ( Settings . Taskbar . EnableTaskbar ? taskbar . GetAbsoluteHeight ( ) : 0 ) ;
logger . Info ( "Re-initializing shell..." ) ;
2019-10-01 11:30:53 +02:00
actionCenter . InitializeBounds ( ) ;
taskbar . InitializeBounds ( ) ;
logger . Info ( "Desktop successfully restored." ) ;
}
2019-10-09 14:04:27 +02:00
private void ApplicationMonitor_TerminationFailed ( IEnumerable < RunningApplication > applications )
{
2019-10-11 15:46:15 +02:00
var applicationList = string . Join ( Environment . NewLine , applications . Select ( a = > $"- {a.Name}" ) ) ;
2020-06-29 19:29:48 +02:00
var message = $"{text.Get(TextKey.LockScreen_ApplicationsMessage)}{Environment.NewLine}{Environment.NewLine}{applicationList}" ;
2019-10-11 15:46:15 +02:00
var title = text . Get ( TextKey . LockScreen_Title ) ;
2020-06-29 19:29:48 +02:00
var allowOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_ApplicationsAllowOption ) } ;
var terminateOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_ApplicationsTerminateOption ) } ;
2019-10-11 15:46:15 +02:00
2020-06-29 19:29:48 +02:00
logger . Warn ( "Detected termination failure of blacklisted application(s)!" ) ;
2019-10-11 15:46:15 +02:00
2020-06-29 19:29:48 +02:00
var result = ShowLockScreen ( message , title , new [ ] { allowOption , terminateOption } ) ;
2019-10-11 15:46:15 +02:00
if ( result . OptionId = = allowOption . Id )
{
logger . Info ( $"The blacklisted application(s) {string.Join(" , ", applications.Select(a => $" ' { a . Name } ' "))} will be temporarily allowed." ) ;
}
else if ( result . OptionId = = terminateOption . Id )
{
2019-12-19 15:02:40 +01:00
logger . Info ( "Attempting to shutdown as requested by the user..." ) ;
2019-10-11 15:46:15 +02:00
TryRequestShutdown ( ) ;
}
2019-10-09 14:04:27 +02:00
}
2018-06-21 07:56:25 +02:00
private void Browser_ConfigurationDownloadRequested ( string fileName , DownloadEventArgs args )
{
2020-09-15 17:16:48 +02:00
var allow = false ;
2020-09-10 12:35:58 +02:00
var hasQuitPassword = ! string . IsNullOrWhiteSpace ( Settings . Security . QuitPasswordHash ) ;
var hasUrl = ! string . IsNullOrWhiteSpace ( Settings . Security . ReconfigurationUrl ) ;
if ( hasQuitPassword )
{
if ( hasUrl )
{
var expression = Regex . Escape ( Settings . Security . ReconfigurationUrl ) . Replace ( @"\*" , ".*" ) ;
var regex = new Regex ( $"^{expression}$" , RegexOptions . IgnoreCase ) ;
2021-04-19 10:49:02 +02:00
var sebUrl = args . Url . Replace ( Uri . UriSchemeHttps , context . AppConfig . SebUriSchemeSecure ) . Replace ( Uri . UriSchemeHttp , context . AppConfig . SebUriScheme ) ;
2020-09-10 12:35:58 +02:00
2020-10-27 19:50:03 +01:00
allow = Settings . Security . AllowReconfiguration & & ( regex . IsMatch ( args . Url ) | | regex . IsMatch ( sebUrl ) ) ;
2020-09-10 12:35:58 +02:00
}
else
{
2021-04-19 10:49:02 +02:00
logger . Warn ( "The active configuration does not contain a valid reconfiguration URL!" ) ;
2020-09-10 12:35:58 +02:00
}
}
2020-09-15 17:16:48 +02:00
else
{
allow = Settings . ConfigurationMode = = ConfigurationMode . ConfigureClient | | Settings . Security . AllowReconfiguration ;
}
2020-09-10 12:35:58 +02:00
if ( allow )
2018-06-21 07:56:25 +02:00
{
2019-09-04 15:12:59 +02:00
args . AllowDownload = true ;
args . Callback = Browser_ConfigurationDownloadFinished ;
2020-01-29 10:07:28 +01:00
args . DownloadPath = Path . Combine ( context . AppConfig . TemporaryDirectory , fileName ) ;
2020-02-14 14:43:08 +01:00
splashScreen . Show ( ) ;
splashScreen . BringToForeground ( ) ;
splashScreen . SetIndeterminate ( ) ;
splashScreen . UpdateStatus ( TextKey . OperationStatus_InitializeSession , true ) ;
2019-09-04 15:12:59 +02:00
logger . Info ( $"Allowed download request for configuration file '{fileName}'." ) ;
2018-06-21 07:56:25 +02:00
}
else
{
2019-09-04 15:12:59 +02:00
args . AllowDownload = false ;
2020-02-25 10:41:55 +01:00
logger . Info ( $"Denied download request for configuration file '{fileName}'." ) ;
2018-06-21 07:56:25 +02:00
}
}
2020-09-24 12:55:20 +02:00
private void Browser_ConfigurationDownloadFinished ( bool success , string url , string filePath = null )
2018-06-21 07:56:25 +02:00
{
if ( success )
{
2024-02-29 21:05:43 +01:00
PrepareShutdown ( ) ;
2020-09-24 12:55:20 +02:00
var communication = runtime . RequestReconfiguration ( filePath , url ) ;
2018-08-10 13:23:24 +02:00
if ( communication . Success )
2018-06-21 07:56:25 +02:00
{
logger . Info ( $"Sent reconfiguration request for '{filePath}' to the runtime." ) ;
}
2018-08-10 13:23:24 +02:00
else
2018-06-21 07:56:25 +02:00
{
2018-08-10 13:23:24 +02:00
logger . Error ( $"Failed to communicate reconfiguration request for '{filePath}'!" ) ;
2020-02-14 14:43:08 +01:00
messageBox . Show ( TextKey . MessageBox_ReconfigurationError , TextKey . MessageBox_ReconfigurationErrorTitle , icon : MessageBoxIcon . Error , parent : splashScreen ) ;
splashScreen . Hide ( ) ;
2018-06-21 07:56:25 +02:00
}
}
else
{
logger . Error ( $"Failed to download configuration file '{filePath}'!" ) ;
2020-02-14 14:43:08 +01:00
messageBox . Show ( TextKey . MessageBox_ConfigurationDownloadError , TextKey . MessageBox_ConfigurationDownloadErrorTitle , icon : MessageBoxIcon . Error , parent : splashScreen ) ;
splashScreen . Hide ( ) ;
2018-06-21 07:56:25 +02:00
}
}
2024-02-29 21:05:43 +01:00
private void Browser_LoseFocusRequested ( bool forward )
{
taskbar . Focus ( forward ) ;
}
2023-11-01 13:52:39 +01:00
private void Browser_UserIdentifierDetected ( string identifier )
2020-09-24 12:55:20 +02:00
{
if ( Settings . SessionMode = = SessionMode . Server )
{
2023-11-01 13:52:39 +01:00
var response = Server . SendUserIdentifier ( identifier ) ;
2020-09-24 12:55:20 +02:00
while ( ! response . Success )
{
2023-11-01 13:52:39 +01:00
logger . Error ( $"Failed to communicate user identifier with server! {response.Message}" ) ;
2020-09-24 12:55:20 +02:00
Thread . Sleep ( Settings . Server . RequestAttemptInterval ) ;
2023-11-01 13:52:39 +01:00
response = Server . SendUserIdentifier ( identifier ) ;
2020-09-24 12:55:20 +02:00
}
}
}
private void Browser_TerminationRequested ( )
{
logger . Info ( "Attempting to shutdown as requested by the browser..." ) ;
TryRequestShutdown ( ) ;
}
2020-07-31 19:57:08 +02:00
private void ClientHost_ExamSelectionRequested ( ExamSelectionRequestEventArgs args )
{
logger . Info ( $"Received exam selection request with id '{args.RequestId}'." ) ;
var exams = args . Exams . Select ( e = > new Exam { Id = e . id , LmsName = e . lms , Name = e . name , Url = e . url } ) ;
var dialog = uiFactory . CreateExamSelectionDialog ( exams ) ;
2020-12-03 18:19:18 +01:00
var result = dialog . Show ( ) ;
2020-07-31 19:57:08 +02:00
runtime . SubmitExamSelectionResult ( args . RequestId , result . Success , result . SelectedExam ? . Id ) ;
logger . Info ( $"Exam selection request with id '{args.RequestId}' is complete." ) ;
}
2018-12-14 12:31:31 +01:00
private void ClientHost_MessageBoxRequested ( MessageBoxRequestEventArgs args )
{
logger . Info ( $"Received message box request with id '{args.RequestId}'." ) ;
2019-08-30 14:02:36 +02:00
var action = ( MessageBoxAction ) args . Action ;
var icon = ( MessageBoxIcon ) args . Icon ;
var result = messageBox . Show ( args . Message , args . Title , action , icon , parent : splashScreen ) ;
2018-12-14 12:31:31 +01:00
2019-08-30 14:02:36 +02:00
runtime . SubmitMessageBoxResult ( args . RequestId , ( int ) result ) ;
2018-12-14 12:31:31 +01:00
logger . Info ( $"Message box request with id '{args.RequestId}' yielded result '{result}'." ) ;
}
2018-07-04 09:53:33 +02:00
private void ClientHost_PasswordRequested ( PasswordRequestEventArgs args )
{
2019-01-30 14:43:41 +01:00
var message = default ( TextKey ) ;
var title = default ( TextKey ) ;
2018-07-04 09:53:33 +02:00
logger . Info ( $"Received input request with id '{args.RequestId}' for the {args.Purpose.ToString().ToLower()} password." ) ;
2019-01-30 14:43:41 +01:00
switch ( args . Purpose )
{
case PasswordRequestPurpose . LocalAdministrator :
message = TextKey . PasswordDialog_LocalAdminPasswordRequired ;
title = TextKey . PasswordDialog_LocalAdminPasswordRequiredTitle ;
break ;
case PasswordRequestPurpose . LocalSettings :
message = TextKey . PasswordDialog_LocalSettingsPasswordRequired ;
title = TextKey . PasswordDialog_LocalSettingsPasswordRequiredTitle ;
break ;
case PasswordRequestPurpose . Settings :
message = TextKey . PasswordDialog_SettingsPasswordRequired ;
title = TextKey . PasswordDialog_SettingsPasswordRequiredTitle ;
break ;
}
var dialog = uiFactory . CreatePasswordDialog ( text . Get ( message ) , text . Get ( title ) ) ;
2018-07-04 09:53:33 +02:00
var result = dialog . Show ( ) ;
runtime . SubmitPassword ( args . RequestId , result . Success , result . Password ) ;
logger . Info ( $"Password request with id '{args.RequestId}' was {(result.Success ? " successful " : " aborted by the user ")}." ) ;
2020-02-10 16:47:50 +01:00
}
2019-06-12 08:46:10 +02:00
2020-02-10 16:47:50 +01:00
private void ClientHost_ReconfigurationAborted ( )
{
logger . Info ( "The reconfiguration was aborted by the runtime." ) ;
2020-02-14 14:43:08 +01:00
splashScreen . Hide ( ) ;
2018-07-04 09:53:33 +02:00
}
private void ClientHost_ReconfigurationDenied ( ReconfigurationEventArgs args )
{
logger . Info ( $"The reconfiguration request for '{args.ConfigurationPath}' was denied by the runtime!" ) ;
2019-06-12 08:46:10 +02:00
messageBox . Show ( TextKey . MessageBox_ReconfigurationDenied , TextKey . MessageBox_ReconfigurationDeniedTitle , parent : splashScreen ) ;
2020-02-14 14:43:08 +01:00
splashScreen . Hide ( ) ;
2018-07-04 09:53:33 +02:00
}
2020-07-31 20:35:18 +02:00
private void ClientHost_ServerFailureActionRequested ( ServerFailureActionRequestEventArgs args )
{
logger . Info ( $"Received server failure action request with id '{args.RequestId}'." ) ;
var dialog = uiFactory . CreateServerFailureDialog ( args . Message , args . ShowFallback ) ;
2023-09-05 17:47:05 +02:00
var result = dialog . Show ( ) ;
2020-07-31 20:35:18 +02:00
runtime . SubmitServerFailureActionResult ( args . RequestId , result . Abort , result . Fallback , result . Retry ) ;
2023-09-05 17:47:05 +02:00
logger . Info ( $"Server failure action request with id '{args.RequestId}' is complete, the user chose to {(result.Abort ? " abort " : (result.Fallback ? " fallback " : " retry "))}." ) ;
2020-07-31 20:35:18 +02:00
}
2018-02-20 15:15:26 +01:00
private void ClientHost_Shutdown ( )
{
2018-02-21 14:01:21 +01:00
shutdown . Invoke ( ) ;
2018-02-20 15:15:26 +01:00
}
2019-03-15 11:38:59 +01:00
private void DisplayMonitor_DisplaySettingsChanged ( )
{
2020-02-21 09:38:39 +01:00
logger . Info ( "Re-initializing working area..." ) ;
displayMonitor . InitializePrimaryDisplay ( Settings . Taskbar . EnableTaskbar ? taskbar . GetAbsoluteHeight ( ) : 0 ) ;
2023-02-16 17:54:40 +01:00
2020-02-21 09:38:39 +01:00
logger . Info ( "Re-initializing shell..." ) ;
2019-03-20 10:08:10 +01:00
actionCenter . InitializeBounds ( ) ;
2023-02-16 17:54:40 +01:00
lockScreen ? . InitializeBounds ( ) ;
2019-03-15 11:38:59 +01:00
taskbar . InitializeBounds ( ) ;
2023-02-16 17:54:40 +01:00
2019-03-15 11:38:59 +01:00
logger . Info ( "Desktop successfully restored." ) ;
2021-05-30 20:04:44 +02:00
2021-06-29 17:34:05 +02:00
if ( ! displayMonitor . ValidateConfiguration ( Settings . Display ) . IsAllowed )
2021-05-30 20:04:44 +02:00
{
var continueOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_DisplayConfigurationContinueOption ) } ;
var terminateOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_DisplayConfigurationTerminateOption ) } ;
var message = text . Get ( TextKey . LockScreen_DisplayConfigurationMessage ) ;
var title = text . Get ( TextKey . LockScreen_Title ) ;
var result = ShowLockScreen ( message , title , new [ ] { continueOption , terminateOption } ) ;
if ( result . OptionId = = terminateOption . Id )
{
logger . Info ( "Attempting to shutdown as requested by the user..." ) ;
TryRequestShutdown ( ) ;
}
}
2019-03-15 11:38:59 +01:00
}
2024-05-02 10:30:26 +02:00
private void NetworkAdapter_CredentialsRequired ( CredentialsRequiredEventArgs args )
{
2024-05-21 19:11:42 +02:00
var message = text . Get ( TextKey . CredentialsDialog_WirelessNetworkMessage ) . Replace ( "%%_NAME_%%" , args . NetworkName ) ;
var title = text . Get ( TextKey . CredentialsDialog_WirelessNetworkTitle ) ;
var dialog = uiFactory . CreateCredentialsDialog ( CredentialsDialogPurpose . WirelessNetwork , message , title ) ;
2024-05-02 10:30:26 +02:00
var result = dialog . Show ( ) ;
args . Password = result . Password ;
args . Success = result . Success ;
args . Username = result . Username ;
}
2019-10-01 11:30:53 +02:00
private void Operations_ActionRequired ( ActionRequiredEventArgs args )
{
2019-10-04 16:36:12 +02:00
switch ( args )
{
2019-11-05 10:08:19 +01:00
case ApplicationNotFoundEventArgs a :
AskForApplicationPath ( a ) ;
break ;
case ApplicationInitializationFailedEventArgs a :
InformAboutFailedApplicationInitialization ( a ) ;
break ;
2019-10-04 16:36:12 +02:00
case ApplicationTerminationEventArgs a :
AskForAutomaticApplicationTermination ( a ) ;
break ;
case ApplicationTerminationFailedEventArgs a :
InformAboutFailedApplicationTermination ( a ) ;
break ;
}
2019-10-01 11:30:53 +02:00
}
2018-10-03 14:35:27 +02:00
private void Operations_ProgressChanged ( ProgressChangedEventArgs args )
{
if ( args . CurrentValue . HasValue )
{
2020-02-14 14:43:08 +01:00
splashScreen . SetValue ( args . CurrentValue . Value ) ;
2018-10-03 14:35:27 +02:00
}
if ( args . IsIndeterminate = = true )
{
2020-02-14 14:43:08 +01:00
splashScreen . SetIndeterminate ( ) ;
2018-10-03 14:35:27 +02:00
}
if ( args . MaxValue . HasValue )
{
2020-02-14 14:43:08 +01:00
splashScreen . SetMaxValue ( args . MaxValue . Value ) ;
2018-10-03 14:35:27 +02:00
}
if ( args . Progress = = true )
{
2020-02-14 14:43:08 +01:00
splashScreen . Progress ( ) ;
2018-10-03 14:35:27 +02:00
}
if ( args . Regress = = true )
{
2020-02-14 14:43:08 +01:00
splashScreen . Regress ( ) ;
2018-10-03 14:35:27 +02:00
}
}
private void Operations_StatusChanged ( TextKey status )
{
2020-02-14 14:43:08 +01:00
splashScreen . UpdateStatus ( status , true ) ;
2018-10-03 14:35:27 +02:00
}
2023-09-01 12:28:03 +02:00
private void Registry_ValueChanged ( string key , string name , object oldValue , object newValue )
{
if ( key = = RegistryValue . UserHive . Cursors_Key )
{
HandleCursorRegistryChange ( key , name , oldValue , newValue ) ;
}
else if ( key = = RegistryValue . MachineHive . EaseOfAccess_Key )
{
HandleEaseOfAccessRegistryChange ( key , name , oldValue , newValue ) ;
}
}
private void HandleCursorRegistryChange ( string key , string name , object oldValue , object newValue )
{
logger . Warn ( $@"The cursor registry value '{key}\{name}' has changed from '{oldValue}' to '{newValue}'! Attempting to show lock screen..." ) ;
if ( ! sessionLocked )
{
var message = text . Get ( TextKey . LockScreen_CursorMessage ) ;
var title = text . Get ( TextKey . LockScreen_Title ) ;
var continueOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_CursorContinueOption ) } ;
var terminateOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_CursorTerminateOption ) } ;
sessionLocked = true ;
registry . StopMonitoring ( key , name ) ;
var result = ShowLockScreen ( message , title , new [ ] { continueOption , terminateOption } ) ;
if ( result . OptionId = = continueOption . Id )
{
logger . Info ( "The session will be allowed to resume as requested by the user..." ) ;
}
else if ( result . OptionId = = terminateOption . Id )
{
logger . Info ( "Attempting to shutdown as requested by the user..." ) ;
TryRequestShutdown ( ) ;
}
sessionLocked = false ;
}
else
{
logger . Info ( "Lock screen is already active." ) ;
}
}
private void HandleEaseOfAccessRegistryChange ( string key , string name , object oldValue , object newValue )
2022-12-21 05:37:03 +01:00
{
2023-09-01 12:28:03 +02:00
logger . Warn ( $@"The ease of access registry value '{key}\{name}' has changed from '{oldValue}' to '{newValue}'! Attempting to show lock screen..." ) ;
2022-12-21 05:37:03 +01:00
if ( ! sessionLocked )
{
var message = text . Get ( TextKey . LockScreen_EaseOfAccessMessage ) ;
var title = text . Get ( TextKey . LockScreen_Title ) ;
var continueOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_EaseOfAccessContinueOption ) } ;
var terminateOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_EaseOfAccessTerminateOption ) } ;
sessionLocked = true ;
2023-09-01 12:28:03 +02:00
registry . StopMonitoring ( key , name ) ;
2022-12-21 05:37:03 +01:00
var result = ShowLockScreen ( message , title , new [ ] { continueOption , terminateOption } ) ;
if ( result . OptionId = = continueOption . Id )
{
logger . Info ( "The session will be allowed to resume as requested by the user..." ) ;
}
else if ( result . OptionId = = terminateOption . Id )
{
logger . Info ( "Attempting to shutdown as requested by the user..." ) ;
TryRequestShutdown ( ) ;
}
sessionLocked = false ;
}
else
{
logger . Info ( "Lock screen is already active." ) ;
}
}
2018-02-27 15:28:54 +01:00
private void Runtime_ConnectionLost ( )
{
logger . Error ( "Lost connection to the runtime!" ) ;
2018-03-14 12:07:20 +01:00
messageBox . Show ( TextKey . MessageBox_ApplicationError , TextKey . MessageBox_ApplicationErrorTitle , icon : MessageBoxIcon . Error ) ;
2018-02-27 15:28:54 +01:00
shutdown . Invoke ( ) ;
}
2022-09-02 14:56:49 +02:00
private void Server_LockScreenConfirmed ( )
{
logger . Info ( "Closing lock screen as requested by the server..." ) ;
lockScreen ? . Cancel ( ) ;
}
private void Server_LockScreenRequested ( string message )
{
2022-09-02 15:00:51 +02:00
logger . Info ( "Attempting to show lock screen as requested by the server..." ) ;
if ( ! sessionLocked )
{
sessionLocked = true ;
ShowLockScreen ( message , text . Get ( TextKey . LockScreen_Title ) , Enumerable . Empty < LockScreenOption > ( ) ) ;
sessionLocked = false ;
}
else
{
logger . Info ( "Lock screen is already active." ) ;
}
2022-09-02 14:56:49 +02:00
}
2020-08-01 17:55:18 +02:00
private void Server_TerminationRequested ( )
{
logger . Info ( "Attempting to shutdown as requested by the server..." ) ;
TryRequestShutdown ( ) ;
}
2019-03-15 11:38:59 +01:00
private void Shell_QuitButtonClicked ( System . ComponentModel . CancelEventArgs args )
2019-03-29 07:46:21 +01:00
{
2019-10-11 15:46:15 +02:00
PauseActivators ( ) ;
2019-03-29 07:46:21 +01:00
args . Cancel = ! TryInitiateShutdown ( ) ;
2019-10-11 15:46:15 +02:00
ResumeActivators ( ) ;
2019-03-29 07:46:21 +01:00
}
2024-01-11 19:01:56 +01:00
private void SystemMonitor_SessionChanged ( )
2020-06-29 19:29:48 +02:00
{
2020-10-23 09:31:23 +02:00
var allow = ! Settings . Service . IgnoreService & & ( ! Settings . Service . DisableUserLock | | ! Settings . Service . DisableUserSwitch ) ;
2024-01-11 19:01:56 +01:00
var disable = Settings . Security . DisableSessionChangeLockScreen ;
2020-06-29 19:29:48 +02:00
var message = text . Get ( TextKey . LockScreen_UserSessionMessage ) ;
var title = text . Get ( TextKey . LockScreen_Title ) ;
var continueOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_UserSessionContinueOption ) } ;
var terminateOption = new LockScreenOption { Text = text . Get ( TextKey . LockScreen_UserSessionTerminateOption ) } ;
2024-01-11 19:01:56 +01:00
if ( allow | | disable )
2020-06-29 19:29:48 +02:00
{
2024-01-11 19:01:56 +01:00
logger . Info ( $"Detected user session change, but {(allow ? " session locking and / or switching is allowed " : " lock screen is deactivated ")}." ) ;
2020-10-23 09:31:23 +02:00
}
else
{
2024-01-11 19:01:56 +01:00
logger . Warn ( "Detected user session change!" ) ;
2020-10-23 09:31:23 +02:00
if ( ! sessionLocked )
{
sessionLocked = true ;
2020-06-29 19:29:48 +02:00
2020-10-23 09:31:23 +02:00
var result = ShowLockScreen ( message , title , new [ ] { continueOption , terminateOption } ) ;
2020-06-29 19:29:48 +02:00
2020-10-23 09:31:23 +02:00
if ( result . OptionId = = terminateOption . Id )
{
logger . Info ( "Attempting to shutdown as requested by the user..." ) ;
TryRequestShutdown ( ) ;
}
sessionLocked = false ;
}
else
2020-06-29 19:29:48 +02:00
{
2020-10-23 09:31:23 +02:00
logger . Info ( "Lock screen is already active." ) ;
2020-06-29 19:29:48 +02:00
}
}
}
2024-02-29 21:05:43 +01:00
private void Taskbar_LoseFocusRequested ( bool forward )
{
Browser . Focus ( forward ) ;
}
2019-03-29 07:46:21 +01:00
private void TerminationActivator_Activated ( )
{
2019-10-11 15:46:15 +02:00
PauseActivators ( ) ;
2019-03-29 07:46:21 +01:00
TryInitiateShutdown ( ) ;
2019-10-11 15:46:15 +02:00
ResumeActivators ( ) ;
2019-03-29 07:46:21 +01:00
}
2019-10-04 16:36:12 +02:00
private void AskForAutomaticApplicationTermination ( ApplicationTerminationEventArgs args )
{
var nl = Environment . NewLine ;
var applicationList = string . Join ( Environment . NewLine , args . RunningApplications . Select ( a = > a . Name ) ) ;
var warning = text . Get ( TextKey . MessageBox_ApplicationAutoTerminationDataLossWarning ) ;
var message = $"{text.Get(TextKey.MessageBox_ApplicationAutoTerminationQuestion)}{nl}{nl}{warning}{nl}{nl}{applicationList}" ;
var title = text . Get ( TextKey . MessageBox_ApplicationAutoTerminationQuestionTitle ) ;
var result = messageBox . Show ( message , title , MessageBoxAction . YesNo , MessageBoxIcon . Question , parent : splashScreen ) ;
args . TerminateProcesses = result = = MessageBoxResult . Yes ;
}
2019-11-05 10:08:19 +01:00
private void AskForApplicationPath ( ApplicationNotFoundEventArgs args )
{
2019-12-06 17:42:46 +01:00
var message = text . Get ( TextKey . FolderDialog_ApplicationLocation ) . Replace ( "%%NAME%%" , args . DisplayName ) . Replace ( "%%EXECUTABLE%%" , args . ExecutableName ) ;
2020-01-24 11:07:52 +01:00
var result = fileSystemDialog . Show ( FileSystemElement . Folder , FileSystemOperation . Open , message : message , parent : splashScreen ) ;
2019-12-06 17:42:46 +01:00
if ( result . Success )
{
2020-01-22 16:08:57 +01:00
args . CustomPath = result . FullPath ;
2019-12-06 17:42:46 +01:00
args . Success = true ;
}
2019-11-05 10:08:19 +01:00
}
private void InformAboutFailedApplicationInitialization ( ApplicationInitializationFailedEventArgs args )
{
var messageKey = TextKey . MessageBox_ApplicationInitializationFailure ;
var titleKey = TextKey . MessageBox_ApplicationInitializationFailureTitle ;
switch ( args . Result )
{
case FactoryResult . NotFound :
messageKey = TextKey . MessageBox_ApplicationNotFound ;
titleKey = TextKey . MessageBox_ApplicationNotFoundTitle ;
break ;
}
var message = text . Get ( messageKey ) . Replace ( "%%NAME%%" , $"'{args.DisplayName}' ({args.ExecutableName})" ) ;
var title = text . Get ( titleKey ) ;
messageBox . Show ( message , title , icon : MessageBoxIcon . Error , parent : splashScreen ) ;
}
2019-10-04 16:36:12 +02:00
private void InformAboutFailedApplicationTermination ( ApplicationTerminationFailedEventArgs args )
{
var applicationList = string . Join ( Environment . NewLine , args . Applications . Select ( a = > a . Name ) ) ;
var message = $"{text.Get(TextKey.MessageBox_ApplicationTerminationFailure)}{Environment.NewLine}{Environment.NewLine}{applicationList}" ;
var title = text . Get ( TextKey . MessageBox_ApplicationTerminationFailureTitle ) ;
messageBox . Show ( message , title , icon : MessageBoxIcon . Error , parent : splashScreen ) ;
}
2019-10-11 15:46:15 +02:00
private void PauseActivators ( )
{
foreach ( var activator in context . Activators )
{
activator . Pause ( ) ;
}
}
private void ResumeActivators ( )
{
foreach ( var activator in context . Activators )
{
activator . Resume ( ) ;
}
}
2020-06-29 19:29:48 +02:00
private LockScreenResult ShowLockScreen ( string message , string title , IEnumerable < LockScreenOption > options )
{
var hasQuitPassword = ! string . IsNullOrEmpty ( Settings . Security . QuitPasswordHash ) ;
var result = default ( LockScreenResult ) ;
logger . Info ( "Showing lock screen..." ) ;
PauseActivators ( ) ;
2022-09-02 14:56:49 +02:00
lockScreen = uiFactory . CreateLockScreen ( message , title , options ) ;
2020-06-29 19:29:48 +02:00
lockScreen . Show ( ) ;
2022-08-31 14:11:19 +02:00
if ( Settings . SessionMode = = SessionMode . Server )
{
var response = Server . LockScreen ( message ) ;
if ( ! response . Success )
{
logger . Error ( $"Failed to send lock screen notification to server! Message: {response.Message}." ) ;
}
}
2020-06-29 19:29:48 +02:00
for ( var unlocked = false ; ! unlocked ; )
{
result = lockScreen . WaitForResult ( ) ;
2022-09-02 14:56:49 +02:00
if ( result . Canceled )
2022-08-31 14:11:19 +02:00
{
logger . Info ( "The lock screen has been automaticaly canceled." ) ;
unlocked = true ;
2022-09-02 14:56:49 +02:00
}
2022-08-31 14:11:19 +02:00
else if ( hasQuitPassword )
2020-06-29 19:29:48 +02:00
{
var passwordHash = hashAlgorithm . GenerateHashFor ( result . Password ) ;
var isCorrect = Settings . Security . QuitPasswordHash . Equals ( passwordHash , StringComparison . OrdinalIgnoreCase ) ;
if ( isCorrect )
{
logger . Info ( "The user entered the correct unlock password." ) ;
unlocked = true ;
}
else
{
logger . Info ( "The user entered the wrong unlock password." ) ;
messageBox . Show ( TextKey . MessageBox_InvalidUnlockPassword , TextKey . MessageBox_InvalidUnlockPasswordTitle , icon : MessageBoxIcon . Warning , parent : lockScreen ) ;
}
}
else
{
logger . Warn ( $"No unlock password is defined, allowing user to resume session!" ) ;
unlocked = true ;
}
}
lockScreen . Close ( ) ;
ResumeActivators ( ) ;
logger . Info ( "Closed lock screen." ) ;
2022-08-31 14:11:19 +02:00
if ( Settings . SessionMode = = SessionMode . Server )
{
var response = Server . ConfirmLockScreen ( ) ;
if ( ! response . Success )
{
logger . Error ( $"Failed to send lock screen confirm notification to server! Message: {response.Message}." ) ;
}
}
2020-06-29 19:29:48 +02:00
return result ;
}
2019-03-29 07:46:21 +01:00
private bool TryInitiateShutdown ( )
2018-02-20 15:15:26 +01:00
{
2019-12-20 11:37:07 +01:00
var hasQuitPassword = ! string . IsNullOrEmpty ( Settings . Security . QuitPasswordHash ) ;
2024-02-29 21:05:43 +01:00
var initiateShutdown = false ;
2019-10-11 15:46:15 +02:00
var succes = false ;
2019-01-10 10:04:30 +01:00
if ( hasQuitPassword )
{
2024-02-29 21:05:43 +01:00
initiateShutdown = TryValidateQuitPassword ( ) ;
2019-01-10 10:04:30 +01:00
}
else
{
2024-02-29 21:05:43 +01:00
initiateShutdown = TryConfirmShutdown ( ) ;
2019-01-10 10:04:30 +01:00
}
2018-02-27 15:28:54 +01:00
2024-02-29 21:05:43 +01:00
if ( initiateShutdown )
2018-02-27 15:28:54 +01:00
{
2019-10-11 15:46:15 +02:00
succes = TryRequestShutdown ( ) ;
2018-02-27 15:28:54 +01:00
}
2018-02-20 15:15:26 +01:00
2019-10-11 15:46:15 +02:00
return succes ;
2017-07-26 14:36:20 +02:00
}
2019-01-10 10:04:30 +01:00
private bool TryConfirmShutdown ( )
{
var result = messageBox . Show ( TextKey . MessageBox_Quit , TextKey . MessageBox_QuitTitle , MessageBoxAction . YesNo , MessageBoxIcon . Question ) ;
var quit = result = = MessageBoxResult . Yes ;
2022-07-18 21:37:04 +02:00
2019-01-10 10:04:30 +01:00
if ( quit )
{
logger . Info ( "The user chose to terminate the application." ) ;
}
return quit ;
}
private bool TryValidateQuitPassword ( )
{
var dialog = uiFactory . CreatePasswordDialog ( TextKey . PasswordDialog_QuitPasswordRequired , TextKey . PasswordDialog_QuitPasswordRequiredTitle ) ;
var result = dialog . Show ( ) ;
if ( result . Success )
{
var passwordHash = hashAlgorithm . GenerateHashFor ( result . Password ) ;
2019-12-20 11:37:07 +01:00
var isCorrect = Settings . Security . QuitPasswordHash . Equals ( passwordHash , StringComparison . OrdinalIgnoreCase ) ;
2019-01-10 10:04:30 +01:00
if ( isCorrect )
{
logger . Info ( "The user entered the correct quit password, the application will now terminate." ) ;
}
else
{
logger . Info ( "The user entered the wrong quit password." ) ;
messageBox . Show ( TextKey . MessageBox_InvalidQuitPassword , TextKey . MessageBox_InvalidQuitPasswordTitle , icon : MessageBoxIcon . Warning ) ;
}
return isCorrect ;
}
return false ;
}
2019-10-11 15:46:15 +02:00
private bool TryRequestShutdown ( )
{
2024-02-29 21:05:43 +01:00
PrepareShutdown ( ) ;
2019-10-11 15:46:15 +02:00
var communication = runtime . RequestShutdown ( ) ;
if ( ! communication . Success )
{
logger . Error ( "Failed to communicate shutdown request to the runtime!" ) ;
messageBox . Show ( TextKey . MessageBox_QuitError , TextKey . MessageBox_QuitErrorTitle , icon : MessageBoxIcon . Error ) ;
}
return communication . Success ;
}
2017-07-26 14:36:20 +02:00
}
}