Restore SEBPatch
This commit is contained in:
18
SafeExamBrowser.Client/App.config
Normal file
18
SafeExamBrowser.Client/App.config
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Win32.Registry" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
101
SafeExamBrowser.Client/App.cs
Normal file
101
SafeExamBrowser.Client/App.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client
|
||||
{
|
||||
public class App : Application
|
||||
{
|
||||
private const int ILMCM_CHECKLAYOUTANDTIPENABLED = 0x00001;
|
||||
private const int ILMCM_LANGUAGEBAROFF = 0x00002;
|
||||
|
||||
private static readonly Mutex Mutex = new Mutex(true, AppConfig.CLIENT_MUTEX_NAME);
|
||||
private readonly CompositionRoot instances = new CompositionRoot();
|
||||
|
||||
[STAThread]
|
||||
public static void Main()
|
||||
{
|
||||
try
|
||||
{
|
||||
StartApplication();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(e.Message + "\n\n" + e.StackTrace, "Fatal Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Mutex.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void StartApplication()
|
||||
{
|
||||
if (NoInstanceRunning())
|
||||
{
|
||||
new App().Run();
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("You can only run one instance of SEB at a time.", "Startup Not Allowed", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool NoInstanceRunning()
|
||||
{
|
||||
return Mutex.WaitOne(TimeSpan.Zero, true);
|
||||
}
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
|
||||
ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||
|
||||
// We need to manually initialize a monitor in order to prevent Windows from automatically doing so and thus rendering an input lanuage
|
||||
// switch in the bottom right corner of the desktop. This must be done before any UI element is initialized or rendered on the screen.
|
||||
InitLocalMsCtfMonitor(ILMCM_CHECKLAYOUTANDTIPENABLED | ILMCM_LANGUAGEBAROFF);
|
||||
|
||||
instances.BuildObjectGraph(Shutdown);
|
||||
instances.LogStartupInformation();
|
||||
|
||||
var success = instances.ClientController.TryStart();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public new void Shutdown()
|
||||
{
|
||||
void shutdown()
|
||||
{
|
||||
instances.ClientController.Terminate();
|
||||
instances.LogShutdownInformation();
|
||||
|
||||
UninitLocalMsCtfMonitor();
|
||||
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
Dispatcher.InvokeAsync(shutdown);
|
||||
}
|
||||
|
||||
[DllImport("MsCtfMonitor.dll", SetLastError = true)]
|
||||
private static extern IntPtr InitLocalMsCtfMonitor(int dwFlags);
|
||||
|
||||
[DllImport("MsCtfMonitor.dll", SetLastError = true)]
|
||||
private static extern IntPtr UninitLocalMsCtfMonitor();
|
||||
}
|
||||
}
|
84
SafeExamBrowser.Client/ClientContext.cs
Normal file
84
SafeExamBrowser.Client/ClientContext.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SafeExamBrowser.Applications.Contracts;
|
||||
using SafeExamBrowser.Browser.Contracts;
|
||||
using SafeExamBrowser.Communication.Contracts.Hosts;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Integrity;
|
||||
using SafeExamBrowser.Proctoring.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
|
||||
namespace SafeExamBrowser.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds all configuration and session data for the client.
|
||||
/// </summary>
|
||||
internal class ClientContext
|
||||
{
|
||||
/// <summary>
|
||||
/// All activators for shell components.
|
||||
/// </summary>
|
||||
internal IList<IActivator> Activators { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All applications allowed for the current session.
|
||||
/// </summary>
|
||||
internal IList<IApplication<IApplicationWindow>> Applications { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The global application configuration.
|
||||
/// </summary>
|
||||
internal AppConfig AppConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The browser application.
|
||||
/// </summary>
|
||||
internal IBrowserApplication Browser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client communication host.
|
||||
/// </summary>
|
||||
internal IClientHost ClientHost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The integrity module.
|
||||
/// </summary>
|
||||
internal IIntegrityModule IntegrityModule { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The proctoring controller to be used if the current session has proctoring enabled.
|
||||
/// </summary>
|
||||
internal IProctoringController Proctoring { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The server proxy to be used if the current session mode is <see cref="SessionMode.Server"/>.
|
||||
/// </summary>
|
||||
internal IServerProxy Server { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifier of the current session.
|
||||
/// </summary>
|
||||
internal Guid SessionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The settings for the current session.
|
||||
/// </summary>
|
||||
internal AppSettings Settings { get; set; }
|
||||
|
||||
internal ClientContext()
|
||||
{
|
||||
Activators = new List<IActivator>();
|
||||
Applications = new List<IApplication<IApplicationWindow>>();
|
||||
}
|
||||
}
|
||||
}
|
1192
SafeExamBrowser.Client/ClientController.cs
Normal file
1192
SafeExamBrowser.Client/ClientController.cs
Normal file
File diff suppressed because it is too large
Load Diff
111
SafeExamBrowser.Client/Communication/ClientHost.cs
Normal file
111
SafeExamBrowser.Client/Communication/ClientHost.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Communication.Contracts;
|
||||
using SafeExamBrowser.Communication.Contracts.Data;
|
||||
using SafeExamBrowser.Communication.Contracts.Events;
|
||||
using SafeExamBrowser.Communication.Contracts.Hosts;
|
||||
using SafeExamBrowser.Communication.Hosts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.Communication
|
||||
{
|
||||
internal class ClientHost : BaseHost, IClientHost
|
||||
{
|
||||
private bool allowConnection;
|
||||
private int processId;
|
||||
|
||||
public Guid AuthenticationToken { private get; set; }
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
public event CommunicationEventHandler<ExamSelectionRequestEventArgs> ExamSelectionRequested;
|
||||
public event CommunicationEventHandler<MessageBoxRequestEventArgs> MessageBoxRequested;
|
||||
public event CommunicationEventHandler<PasswordRequestEventArgs> PasswordRequested;
|
||||
public event CommunicationEventHandler ReconfigurationAborted;
|
||||
public event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationDenied;
|
||||
public event CommunicationEventHandler RuntimeDisconnected;
|
||||
public event CommunicationEventHandler<ServerFailureActionRequestEventArgs> ServerFailureActionRequested;
|
||||
public event CommunicationEventHandler Shutdown;
|
||||
|
||||
public ClientHost(
|
||||
string address,
|
||||
IHostObjectFactory factory,
|
||||
ILogger logger,
|
||||
int processId,
|
||||
int timeout_ms) : base(address, factory, logger, timeout_ms)
|
||||
{
|
||||
this.allowConnection = true;
|
||||
this.processId = processId;
|
||||
}
|
||||
|
||||
protected override bool OnConnect(Guid? token)
|
||||
{
|
||||
var authenticated = AuthenticationToken == token;
|
||||
var accepted = allowConnection && authenticated;
|
||||
|
||||
if (accepted)
|
||||
{
|
||||
allowConnection = false;
|
||||
IsConnected = true;
|
||||
}
|
||||
|
||||
return accepted;
|
||||
}
|
||||
|
||||
protected override void OnDisconnect(Interlocutor interlocutor)
|
||||
{
|
||||
if (interlocutor == Interlocutor.Runtime)
|
||||
{
|
||||
RuntimeDisconnected?.Invoke();
|
||||
IsConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Response OnReceive(Message message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case ExamSelectionRequestMessage m:
|
||||
ExamSelectionRequested?.InvokeAsync(new ExamSelectionRequestEventArgs { Exams = m.Exams, RequestId = m.RequestId });
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
case MessageBoxRequestMessage m:
|
||||
MessageBoxRequested?.InvokeAsync(new MessageBoxRequestEventArgs { Action = m.Action, Icon = m.Icon, Message = m.Message, RequestId = m.RequestId, Title = m.Title });
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
case PasswordRequestMessage m:
|
||||
PasswordRequested?.InvokeAsync(new PasswordRequestEventArgs { Purpose = m.Purpose, RequestId = m.RequestId });
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
case ReconfigurationDeniedMessage m:
|
||||
ReconfigurationDenied?.InvokeAsync(new ReconfigurationEventArgs { ConfigurationPath = m.FilePath });
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
case ServerFailureActionRequestMessage m:
|
||||
ServerFailureActionRequested?.InvokeAsync(new ServerFailureActionRequestEventArgs { Message = m.Message, RequestId = m.RequestId, ShowFallback = m.ShowFallback });
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
}
|
||||
|
||||
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||
}
|
||||
|
||||
protected override Response OnReceive(SimpleMessagePurport message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case SimpleMessagePurport.Authenticate:
|
||||
return new AuthenticationResponse { ProcessId = processId };
|
||||
case SimpleMessagePurport.ReconfigurationAborted:
|
||||
ReconfigurationAborted?.InvokeAsync();
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
case SimpleMessagePurport.Shutdown:
|
||||
Shutdown?.Invoke();
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
}
|
||||
|
||||
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||
}
|
||||
}
|
||||
}
|
396
SafeExamBrowser.Client/CompositionRoot.cs
Normal file
396
SafeExamBrowser.Client/CompositionRoot.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using SafeExamBrowser.Applications;
|
||||
using SafeExamBrowser.Browser;
|
||||
using SafeExamBrowser.Client.Communication;
|
||||
using SafeExamBrowser.Client.Notifications;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
using SafeExamBrowser.Communication.Contracts;
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.Communication.Hosts;
|
||||
using SafeExamBrowser.Communication.Proxies;
|
||||
using SafeExamBrowser.Configuration.Cryptography;
|
||||
using SafeExamBrowser.Configuration.Integrity;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.OperationModel;
|
||||
using SafeExamBrowser.Core.Operations;
|
||||
using SafeExamBrowser.I18n;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring;
|
||||
using SafeExamBrowser.Monitoring.Applications;
|
||||
using SafeExamBrowser.Monitoring.Display;
|
||||
using SafeExamBrowser.Monitoring.Keyboard;
|
||||
using SafeExamBrowser.Monitoring.Mouse;
|
||||
using SafeExamBrowser.Monitoring.System;
|
||||
using SafeExamBrowser.Proctoring;
|
||||
using SafeExamBrowser.Server;
|
||||
using SafeExamBrowser.Settings.Logging;
|
||||
using SafeExamBrowser.Settings.UserInterface;
|
||||
using SafeExamBrowser.SystemComponents;
|
||||
using SafeExamBrowser.SystemComponents.Audio;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Network;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
||||
using SafeExamBrowser.SystemComponents.Keyboard;
|
||||
using SafeExamBrowser.SystemComponents.Network;
|
||||
using SafeExamBrowser.SystemComponents.PowerSupply;
|
||||
using SafeExamBrowser.SystemComponents.Registry;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Shared.Activators;
|
||||
using SafeExamBrowser.WindowsApi;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Processes;
|
||||
using Desktop = SafeExamBrowser.UserInterface.Desktop;
|
||||
using Mobile = SafeExamBrowser.UserInterface.Mobile;
|
||||
|
||||
namespace SafeExamBrowser.Client
|
||||
{
|
||||
internal class CompositionRoot
|
||||
{
|
||||
private const int TWO_SECONDS = 2000;
|
||||
private const int FIVE_SECONDS = 5000;
|
||||
|
||||
private Guid authenticationToken;
|
||||
private ClientContext context;
|
||||
private string logFilePath;
|
||||
private LogLevel logLevel;
|
||||
private string runtimeHostUri;
|
||||
private UserInterfaceMode uiMode;
|
||||
|
||||
private IActionCenter actionCenter;
|
||||
private ApplicationMonitor applicationMonitor;
|
||||
private ILogger logger;
|
||||
private IMessageBox messageBox;
|
||||
private INativeMethods nativeMethods;
|
||||
private INetworkAdapter networkAdapter;
|
||||
private IPowerSupply powerSupply;
|
||||
private IRuntimeProxy runtimeProxy;
|
||||
private ISystemInfo systemInfo;
|
||||
private ITaskbar taskbar;
|
||||
private ITaskview taskview;
|
||||
private IUserInfo userInfo;
|
||||
private IText text;
|
||||
private IUserInterfaceFactory uiFactory;
|
||||
|
||||
internal ClientController ClientController { get; private set; }
|
||||
|
||||
internal void BuildObjectGraph(Action shutdown)
|
||||
{
|
||||
ValidateCommandLineArguments();
|
||||
|
||||
InitializeLogging();
|
||||
InitializeText();
|
||||
|
||||
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
|
||||
var registry = new Registry(ModuleLogger(nameof(Registry)));
|
||||
|
||||
uiFactory = BuildUserInterfaceFactory();
|
||||
actionCenter = uiFactory.CreateActionCenter();
|
||||
context = new ClientContext();
|
||||
messageBox = BuildMessageBox();
|
||||
nativeMethods = new NativeMethods();
|
||||
applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, processFactory);
|
||||
networkAdapter = new NetworkAdapter(ModuleLogger(nameof(NetworkAdapter)), nativeMethods);
|
||||
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client);
|
||||
systemInfo = new SystemInfo(registry);
|
||||
taskbar = uiFactory.CreateTaskbar(ModuleLogger("Taskbar"));
|
||||
taskview = uiFactory.CreateTaskview();
|
||||
userInfo = new UserInfo(ModuleLogger(nameof(UserInfo)));
|
||||
|
||||
var applicationFactory = new ApplicationFactory(applicationMonitor, ModuleLogger(nameof(ApplicationFactory)), nativeMethods, processFactory, new Registry(ModuleLogger(nameof(Registry))));
|
||||
var clipboard = new Clipboard(ModuleLogger(nameof(Clipboard)), nativeMethods);
|
||||
var coordinator = new Coordinator();
|
||||
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
|
||||
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
||||
var fileSystemDialog = BuildFileSystemDialog();
|
||||
var hashAlgorithm = new HashAlgorithm();
|
||||
var sentinel = new SystemSentinel(ModuleLogger(nameof(SystemSentinel)), nativeMethods, registry);
|
||||
var splashScreen = uiFactory.CreateSplashScreen();
|
||||
|
||||
var operations = new Queue<IOperation>();
|
||||
|
||||
operations.Enqueue(new I18nOperation(logger, text));
|
||||
operations.Enqueue(new RuntimeConnectionOperation(context, logger, runtimeProxy, authenticationToken));
|
||||
operations.Enqueue(new ConfigurationOperation(context, logger, runtimeProxy));
|
||||
operations.Enqueue(new DelegateOperation(UpdateAppConfig));
|
||||
operations.Enqueue(new DelegateOperation(BuildIntegrityModule));
|
||||
operations.Enqueue(new DelegateOperation(BuildPowerSupply));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation));
|
||||
operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
|
||||
operations.Enqueue(new ApplicationOperation(context, applicationFactory, applicationMonitor, logger, text));
|
||||
operations.Enqueue(new DisplayMonitorOperation(context, displayMonitor, logger, taskbar));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildServerOperation));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildProctoringOperation));
|
||||
operations.Enqueue(new ClipboardOperation(context, clipboard, logger));
|
||||
|
||||
var sequence = new OperationSequence(logger, operations);
|
||||
|
||||
ClientController = new ClientController(
|
||||
actionCenter,
|
||||
applicationMonitor,
|
||||
context,
|
||||
coordinator,
|
||||
displayMonitor,
|
||||
explorerShell,
|
||||
fileSystemDialog,
|
||||
hashAlgorithm,
|
||||
logger,
|
||||
messageBox,
|
||||
networkAdapter,
|
||||
sequence,
|
||||
runtimeProxy,
|
||||
shutdown,
|
||||
splashScreen,
|
||||
sentinel,
|
||||
taskbar,
|
||||
text,
|
||||
uiFactory);
|
||||
}
|
||||
|
||||
internal void LogStartupInformation()
|
||||
{
|
||||
logger.Log($"# New client instance started at {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
|
||||
logger.Log(string.Empty);
|
||||
}
|
||||
|
||||
internal void LogShutdownInformation()
|
||||
{
|
||||
logger?.Log($"# Client instance terminated at {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
|
||||
}
|
||||
|
||||
private void ValidateCommandLineArguments()
|
||||
{
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
var hasFive = args?.Length >= 5;
|
||||
var valid = false;
|
||||
|
||||
if (hasFive)
|
||||
{
|
||||
var logFilePath = Encoding.UTF8.GetString(Convert.FromBase64String(args[1]));
|
||||
var hasLogFilePath = Uri.TryCreate(logFilePath, UriKind.Absolute, out var filePath) && filePath.IsFile;
|
||||
var hasLogLevel = Enum.TryParse(args[2], out LogLevel logLevel);
|
||||
var hasHostUri = Uri.TryCreate(args[3], UriKind.Absolute, out var runtimeHostUri) && runtimeHostUri.IsWellFormedOriginalString();
|
||||
var hasAuthenticationToken = Guid.TryParse(args[4], out var authenticationToken);
|
||||
|
||||
if (hasLogFilePath && hasLogLevel && hasHostUri && hasAuthenticationToken)
|
||||
{
|
||||
this.authenticationToken = authenticationToken;
|
||||
this.logFilePath = logFilePath;
|
||||
this.logLevel = logLevel;
|
||||
this.runtimeHostUri = args[3];
|
||||
this.uiMode = args.Length == 6 && Enum.TryParse(args[5], out uiMode) ? uiMode : UserInterfaceMode.Desktop;
|
||||
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
throw new ArgumentException("Invalid arguments! Required: SafeExamBrowser.Client.exe <logfile path> <log level> <host URI> <token>");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeLogging()
|
||||
{
|
||||
var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), logFilePath);
|
||||
|
||||
logFileWriter.Initialize();
|
||||
|
||||
logger = new Logger();
|
||||
logger.LogLevel = logLevel;
|
||||
logger.Subscribe(logFileWriter);
|
||||
}
|
||||
|
||||
private void InitializeText()
|
||||
{
|
||||
text = new Text(ModuleLogger(nameof(Text)));
|
||||
}
|
||||
|
||||
private IOperation BuildBrowserOperation()
|
||||
{
|
||||
var fileSystemDialog = BuildFileSystemDialog();
|
||||
var keyGenerator = new KeyGenerator(context.AppConfig, context.IntegrityModule, ModuleLogger(nameof(KeyGenerator)));
|
||||
var moduleLogger = ModuleLogger(nameof(BrowserApplication));
|
||||
var browser = new BrowserApplication(
|
||||
context.AppConfig,
|
||||
context.Settings.Browser,
|
||||
fileSystemDialog,
|
||||
new HashAlgorithm(),
|
||||
keyGenerator,
|
||||
messageBox,
|
||||
moduleLogger,
|
||||
nativeMethods,
|
||||
context.Settings.SessionMode,
|
||||
text,
|
||||
uiFactory);
|
||||
var operation = new BrowserOperation(actionCenter, context, logger, taskbar, taskview, uiFactory);
|
||||
|
||||
context.Browser = browser;
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private IOperation BuildClientHostOperation()
|
||||
{
|
||||
var processId = Process.GetCurrentProcess().Id;
|
||||
var factory = new HostObjectFactory();
|
||||
var clientHost = new ClientHost(context.AppConfig.ClientAddress, factory, ModuleLogger(nameof(ClientHost)), processId, FIVE_SECONDS);
|
||||
var operation = new CommunicationHostOperation(clientHost, logger);
|
||||
|
||||
context.ClientHost = clientHost;
|
||||
context.ClientHost.AuthenticationToken = authenticationToken;
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private void BuildIntegrityModule()
|
||||
{
|
||||
context.IntegrityModule = new IntegrityModule(context.AppConfig, ModuleLogger(nameof(IntegrityModule)));
|
||||
}
|
||||
|
||||
private void BuildPowerSupply()
|
||||
{
|
||||
powerSupply = new PowerSupply(ModuleLogger(nameof(PowerSupply)), context.Settings.PowerSupply);
|
||||
}
|
||||
|
||||
private IOperation BuildKeyboardInterceptorOperation()
|
||||
{
|
||||
var keyboardInterceptor = new KeyboardInterceptor(ModuleLogger(nameof(KeyboardInterceptor)), nativeMethods, context.Settings.Keyboard);
|
||||
var operation = new KeyboardInterceptorOperation(context, keyboardInterceptor, logger);
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private IOperation BuildMouseInterceptorOperation()
|
||||
{
|
||||
var mouseInterceptor = new MouseInterceptor(ModuleLogger(nameof(MouseInterceptor)), nativeMethods, context.Settings.Mouse);
|
||||
var operation = new MouseInterceptorOperation(context, logger, mouseInterceptor);
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private IOperation BuildProctoringOperation()
|
||||
{
|
||||
var controller = new ProctoringController(
|
||||
context.AppConfig,
|
||||
applicationMonitor,
|
||||
context.Browser,
|
||||
new FileSystem(),
|
||||
ModuleLogger(nameof(ProctoringController)),
|
||||
nativeMethods,
|
||||
context.Server,
|
||||
text,
|
||||
uiFactory);
|
||||
var operation = new ProctoringOperation(actionCenter, context, controller, logger, taskbar, uiFactory);
|
||||
|
||||
context.Proctoring = controller;
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private IOperation BuildServerOperation()
|
||||
{
|
||||
var keyGenerator = new KeyGenerator(context.AppConfig, context.IntegrityModule, ModuleLogger(nameof(KeyGenerator)));
|
||||
var server = new ServerProxy(context.AppConfig, keyGenerator, ModuleLogger(nameof(ServerProxy)), systemInfo, userInfo, powerSupply, networkAdapter);
|
||||
var operation = new ServerOperation(context, logger, server);
|
||||
|
||||
context.Server = server;
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private IOperation BuildShellOperation()
|
||||
{
|
||||
var aboutNotification = new AboutNotification(context.AppConfig, text, uiFactory);
|
||||
var audio = new Audio(context.Settings.Audio, ModuleLogger(nameof(Audio)));
|
||||
var keyboard = new Keyboard(ModuleLogger(nameof(Keyboard)));
|
||||
var logNotification = new LogNotification(logger, text, uiFactory);
|
||||
var operation = new ShellOperation(
|
||||
actionCenter,
|
||||
audio,
|
||||
aboutNotification,
|
||||
context,
|
||||
keyboard,
|
||||
logger,
|
||||
logNotification,
|
||||
nativeMethods,
|
||||
networkAdapter,
|
||||
powerSupply,
|
||||
systemInfo,
|
||||
taskbar,
|
||||
taskview,
|
||||
text,
|
||||
uiFactory);
|
||||
|
||||
context.Activators.Add(new ActionCenterKeyboardActivator(ModuleLogger(nameof(ActionCenterKeyboardActivator)), nativeMethods));
|
||||
context.Activators.Add(new ActionCenterTouchActivator(ModuleLogger(nameof(ActionCenterTouchActivator)), nativeMethods));
|
||||
context.Activators.Add(new TaskbarKeyboardActivator(ModuleLogger(nameof(TaskbarKeyboardActivator)), nativeMethods));
|
||||
context.Activators.Add(new TaskviewKeyboardActivator(ModuleLogger(nameof(TaskviewKeyboardActivator)), nativeMethods));
|
||||
context.Activators.Add(new TerminationActivator(ModuleLogger(nameof(TerminationActivator)), nativeMethods));
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private IFileSystemDialog BuildFileSystemDialog()
|
||||
{
|
||||
switch (uiMode)
|
||||
{
|
||||
case UserInterfaceMode.Mobile:
|
||||
return new Mobile.FileSystemDialogFactory(systemInfo, text);
|
||||
default:
|
||||
return new Desktop.FileSystemDialogFactory(systemInfo, text);
|
||||
}
|
||||
}
|
||||
|
||||
private IMessageBox BuildMessageBox()
|
||||
{
|
||||
switch (uiMode)
|
||||
{
|
||||
case UserInterfaceMode.Mobile:
|
||||
return new Mobile.MessageBoxFactory(text);
|
||||
default:
|
||||
return new Desktop.MessageBoxFactory(text);
|
||||
}
|
||||
}
|
||||
|
||||
private IUserInterfaceFactory BuildUserInterfaceFactory()
|
||||
{
|
||||
switch (uiMode)
|
||||
{
|
||||
case UserInterfaceMode.Mobile:
|
||||
return new Mobile.UserInterfaceFactory(text);
|
||||
default:
|
||||
return new Desktop.UserInterfaceFactory(text);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAppConfig()
|
||||
{
|
||||
ClientController.UpdateAppConfig();
|
||||
}
|
||||
|
||||
private IModuleLogger ModuleLogger(string moduleInfo)
|
||||
{
|
||||
return new ModuleLogger(logger, moduleInfo);
|
||||
}
|
||||
}
|
||||
}
|
46
SafeExamBrowser.Client/Contracts/ICoordinator.cs
Normal file
46
SafeExamBrowser.Client/Contracts/ICoordinator.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Client.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Coordinates concurrent operations of the client application.
|
||||
/// </summary>
|
||||
internal interface ICoordinator
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether the reconfiguration lock is currently occupied.
|
||||
/// </summary>
|
||||
bool IsReconfigurationLocked();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the session lock is currently occupied.
|
||||
/// </summary>
|
||||
bool IsSessionLocked();
|
||||
|
||||
/// <summary>
|
||||
/// Releases the reconfiguration lock.
|
||||
/// </summary>
|
||||
void ReleaseReconfigurationLock();
|
||||
|
||||
/// <summary>
|
||||
/// Releases the session lock.
|
||||
/// </summary>
|
||||
void ReleaseSessionLock();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to acquire the unique reconfiguration lock. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool RequestReconfigurationLock();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to acquire the unique session lock. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool RequestSessionLock();
|
||||
}
|
||||
}
|
78
SafeExamBrowser.Client/Coordinator.cs
Normal file
78
SafeExamBrowser.Client/Coordinator.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client
|
||||
{
|
||||
internal class Coordinator : ICoordinator
|
||||
{
|
||||
private readonly ConcurrentBag<Guid> reconfiguration;
|
||||
private readonly ConcurrentBag<Guid> session;
|
||||
|
||||
internal Coordinator()
|
||||
{
|
||||
reconfiguration = new ConcurrentBag<Guid>();
|
||||
session = new ConcurrentBag<Guid>();
|
||||
}
|
||||
|
||||
public bool IsReconfigurationLocked()
|
||||
{
|
||||
return !reconfiguration.IsEmpty;
|
||||
}
|
||||
|
||||
public bool IsSessionLocked()
|
||||
{
|
||||
return !session.IsEmpty;
|
||||
}
|
||||
|
||||
public void ReleaseReconfigurationLock()
|
||||
{
|
||||
reconfiguration.TryTake(out _);
|
||||
}
|
||||
|
||||
public void ReleaseSessionLock()
|
||||
{
|
||||
session.TryTake(out _);
|
||||
}
|
||||
|
||||
public bool RequestReconfigurationLock()
|
||||
{
|
||||
var acquired = false;
|
||||
|
||||
lock (reconfiguration)
|
||||
{
|
||||
if (reconfiguration.IsEmpty)
|
||||
{
|
||||
reconfiguration.Add(Guid.NewGuid());
|
||||
acquired = true;
|
||||
}
|
||||
}
|
||||
|
||||
return acquired;
|
||||
}
|
||||
|
||||
public bool RequestSessionLock()
|
||||
{
|
||||
var acquired = false;
|
||||
|
||||
lock (session)
|
||||
{
|
||||
if (session.IsEmpty)
|
||||
{
|
||||
session.Add(Guid.NewGuid());
|
||||
acquired = true;
|
||||
}
|
||||
}
|
||||
|
||||
return acquired;
|
||||
}
|
||||
}
|
||||
}
|
63
SafeExamBrowser.Client/Notifications/AboutNotification.cs
Normal file
63
SafeExamBrowser.Client/Notifications/AboutNotification.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Core.Contracts.Notifications;
|
||||
using SafeExamBrowser.Core.Contracts.Notifications.Events;
|
||||
using SafeExamBrowser.Core.Contracts.Resources.Icons;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
|
||||
namespace SafeExamBrowser.Client.Notifications
|
||||
{
|
||||
internal class AboutNotification : INotification
|
||||
{
|
||||
private readonly AppConfig appConfig;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
private IWindow window;
|
||||
|
||||
public bool CanActivate { get; }
|
||||
public string Tooltip { get; }
|
||||
public IconResource IconResource { get; }
|
||||
|
||||
public event NotificationChangedEventHandler NotificationChanged { add { } remove { } }
|
||||
|
||||
public AboutNotification(AppConfig appConfig, IText text, IUserInterfaceFactory uiFactory)
|
||||
{
|
||||
this.appConfig = appConfig;
|
||||
this.uiFactory = uiFactory;
|
||||
|
||||
CanActivate = true;
|
||||
IconResource = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/AboutNotification.xaml") };
|
||||
Tooltip = text.Get(TextKey.Notification_AboutTooltip);
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (window == default)
|
||||
{
|
||||
window = uiFactory.CreateAboutWindow(appConfig);
|
||||
|
||||
window.Closed += () => window = default;
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
window.BringToForeground();
|
||||
}
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
window?.Close();
|
||||
}
|
||||
}
|
||||
}
|
63
SafeExamBrowser.Client/Notifications/LogNotification.cs
Normal file
63
SafeExamBrowser.Client/Notifications/LogNotification.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Core.Contracts.Notifications;
|
||||
using SafeExamBrowser.Core.Contracts.Notifications.Events;
|
||||
using SafeExamBrowser.Core.Contracts.Resources.Icons;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
|
||||
namespace SafeExamBrowser.Client.Notifications
|
||||
{
|
||||
internal class LogNotification : INotification
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
private IWindow window;
|
||||
|
||||
public bool CanActivate { get; }
|
||||
public string Tooltip { get; }
|
||||
public IconResource IconResource { get; }
|
||||
|
||||
public event NotificationChangedEventHandler NotificationChanged { add { } remove { } }
|
||||
|
||||
public LogNotification(ILogger logger, IText text, IUserInterfaceFactory uiFactory)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.uiFactory = uiFactory;
|
||||
|
||||
CanActivate = true;
|
||||
IconResource = new BitmapIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/LogNotification.ico") };
|
||||
Tooltip = text.Get(TextKey.Notification_LogTooltip);
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (window == default)
|
||||
{
|
||||
window = uiFactory.CreateLogWindow(logger);
|
||||
|
||||
window.Closed += () => window = default;
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
window.BringToForeground();
|
||||
}
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
window?.Close();
|
||||
}
|
||||
}
|
||||
}
|
204
SafeExamBrowser.Client/Operations/ApplicationOperation.cs
Normal file
204
SafeExamBrowser.Client/Operations/ApplicationOperation.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SafeExamBrowser.Applications.Contracts;
|
||||
using SafeExamBrowser.Client.Operations.Events;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||
using SafeExamBrowser.Settings.Applications;
|
||||
using SafeExamBrowser.Settings.Security;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class ApplicationOperation : ClientOperation
|
||||
{
|
||||
private IApplicationFactory factory;
|
||||
private ILogger logger;
|
||||
private IApplicationMonitor monitor;
|
||||
private IText text;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired;
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ApplicationOperation(
|
||||
ClientContext context,
|
||||
IApplicationFactory factory,
|
||||
IApplicationMonitor monitor,
|
||||
ILogger logger,
|
||||
IText text) : base(context)
|
||||
{
|
||||
this.factory = factory;
|
||||
this.monitor = monitor;
|
||||
this.logger = logger;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing applications...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeApplications);
|
||||
|
||||
var result = InitializeApplications();
|
||||
|
||||
if (result == OperationResult.Success)
|
||||
{
|
||||
StartMonitor();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
logger.Info("Finalizing applications...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeApplications);
|
||||
|
||||
FinalizeApplications();
|
||||
StopMonitor();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private OperationResult InitializeApplications()
|
||||
{
|
||||
var initialization = monitor.Initialize(Context.Settings.Applications);
|
||||
var result = OperationResult.Success;
|
||||
|
||||
if (initialization.FailedAutoTerminations.Any())
|
||||
{
|
||||
result = HandleAutoTerminationFailure(initialization.FailedAutoTerminations);
|
||||
}
|
||||
else if (initialization.RunningApplications.Any())
|
||||
{
|
||||
result = TryTerminate(initialization.RunningApplications);
|
||||
}
|
||||
|
||||
if (result == OperationResult.Success)
|
||||
{
|
||||
foreach (var application in Context.Settings.Applications.Whitelist)
|
||||
{
|
||||
Initialize(application);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void Initialize(WhitelistApplication settings)
|
||||
{
|
||||
var result = factory.TryCreate(settings, out var application);
|
||||
|
||||
while (result == FactoryResult.NotFound && settings.AllowCustomPath)
|
||||
{
|
||||
var args = new ApplicationNotFoundEventArgs(settings.DisplayName, settings.ExecutableName);
|
||||
|
||||
ActionRequired?.Invoke(args);
|
||||
|
||||
if (args.Success)
|
||||
{
|
||||
settings.ExecutablePath = args.CustomPath;
|
||||
result = factory.TryCreate(settings, out application);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == FactoryResult.Success)
|
||||
{
|
||||
Context.Applications.Add(application);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Failed to initialize application '{settings.DisplayName}' ({settings.ExecutableName}). Reason: {result}.");
|
||||
ActionRequired?.Invoke(new ApplicationInitializationFailedEventArgs(settings.DisplayName, settings.ExecutableName, result));
|
||||
}
|
||||
}
|
||||
|
||||
private void FinalizeApplications()
|
||||
{
|
||||
foreach (var application in Context.Applications)
|
||||
{
|
||||
application.Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
private OperationResult HandleAutoTerminationFailure(IList<RunningApplication> applications)
|
||||
{
|
||||
logger.Error($"{applications.Count} application(s) could not be automatically terminated: {string.Join(", ", applications.Select(a => a.Name))}");
|
||||
ActionRequired?.Invoke(new ApplicationTerminationFailedEventArgs(applications));
|
||||
|
||||
return OperationResult.Failed;
|
||||
}
|
||||
|
||||
private void StartMonitor()
|
||||
{
|
||||
if (Context.Settings.Security.KioskMode != KioskMode.None)
|
||||
{
|
||||
monitor.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void StopMonitor()
|
||||
{
|
||||
if (Context.Settings.Security.KioskMode != KioskMode.None)
|
||||
{
|
||||
monitor.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private OperationResult TryTerminate(IEnumerable<RunningApplication> runningApplications)
|
||||
{
|
||||
var args = new ApplicationTerminationEventArgs(runningApplications);
|
||||
var failed = new List<RunningApplication>();
|
||||
var result = OperationResult.Success;
|
||||
|
||||
logger.Info($"The following applications need to be terminated: {string.Join(", ", runningApplications.Select(a => a.Name))}.");
|
||||
ActionRequired?.Invoke(args);
|
||||
|
||||
if (args.TerminateProcesses)
|
||||
{
|
||||
logger.Info($"The user chose to automatically terminate all running applications.");
|
||||
|
||||
foreach (var application in runningApplications)
|
||||
{
|
||||
var success = monitor.TryTerminate(application);
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Info($"Successfully terminated application '{application.Name}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
result = OperationResult.Failed;
|
||||
failed.Add(application);
|
||||
logger.Error($"Failed to automatically terminate application '{application.Name}'!");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("The user chose not to automatically terminate all running applications. Aborting...");
|
||||
result = OperationResult.Aborted;
|
||||
}
|
||||
|
||||
if (failed.Any())
|
||||
{
|
||||
ActionRequired?.Invoke(new ApplicationTerminationFailedEventArgs(failed));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
90
SafeExamBrowser.Client/Operations/BrowserOperation.cs
Normal file
90
SafeExamBrowser.Client/Operations/BrowserOperation.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class BrowserOperation : ClientOperation
|
||||
{
|
||||
private readonly IActionCenter actionCenter;
|
||||
private readonly ILogger logger;
|
||||
private readonly ITaskbar taskbar;
|
||||
private readonly ITaskview taskview;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public BrowserOperation(
|
||||
IActionCenter actionCenter,
|
||||
ClientContext context,
|
||||
ILogger logger,
|
||||
ITaskbar taskbar,
|
||||
ITaskview taskview,
|
||||
IUserInterfaceFactory uiFactory) : base(context)
|
||||
{
|
||||
this.actionCenter = actionCenter;
|
||||
this.logger = logger;
|
||||
this.taskbar = taskbar;
|
||||
this.taskview = taskview;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing browser...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeBrowser);
|
||||
|
||||
if (Context.Settings.Browser.EnableBrowser)
|
||||
{
|
||||
Context.Browser.Initialize();
|
||||
|
||||
if (Context.Settings.UserInterface.ActionCenter.EnableActionCenter)
|
||||
{
|
||||
actionCenter.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.ActionCenter), true);
|
||||
}
|
||||
|
||||
if (Context.Settings.UserInterface.Taskbar.EnableTaskbar)
|
||||
{
|
||||
taskbar.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.Taskbar), true);
|
||||
}
|
||||
|
||||
taskview.Add(Context.Browser);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("Browser application is disabled for this session.");
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
logger.Info("Terminating browser...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateBrowser);
|
||||
|
||||
if (Context.Settings.Browser.EnableBrowser)
|
||||
{
|
||||
Context.Browser.Terminate();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("Browser application was disabled for this session.");
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Communication.Contracts.Events;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
/// <summary>
|
||||
/// During application shutdown, it could happen that the client stops its communication host before the runtime had the chance to
|
||||
/// disconnect from it. This operation prevents the described race condition by waiting on the runtime to disconnect from the client.
|
||||
/// </summary>
|
||||
internal class ClientHostDisconnectionOperation : ClientOperation
|
||||
{
|
||||
private ILogger logger;
|
||||
private int timeout_ms;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ClientHostDisconnectionOperation(ClientContext context, ILogger logger, int timeout_ms) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.timeout_ms = timeout_ms;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_WaitRuntimeDisconnection);
|
||||
|
||||
if (Context.ClientHost.IsConnected)
|
||||
{
|
||||
var disconnected = false;
|
||||
var disconnectedEvent = new AutoResetEvent(false);
|
||||
var disconnectedEventHandler = new CommunicationEventHandler(() => disconnectedEvent.Set());
|
||||
|
||||
Context.ClientHost.RuntimeDisconnected += disconnectedEventHandler;
|
||||
|
||||
logger.Info("Waiting for runtime to disconnect from client communication host...");
|
||||
disconnected = disconnectedEvent.WaitOne(timeout_ms);
|
||||
|
||||
Context.ClientHost.RuntimeDisconnected -= disconnectedEventHandler;
|
||||
|
||||
if (disconnected)
|
||||
{
|
||||
logger.Info("The runtime has successfully disconnected from the client communication host.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"The runtime failed to disconnect within {timeout_ms / 1000} seconds!");
|
||||
|
||||
return OperationResult.Failed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("The runtime has already disconnected from the client communication host.");
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
32
SafeExamBrowser.Client/Operations/ClientOperation.cs
Normal file
32
SafeExamBrowser.Client/Operations/ClientOperation.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
/// <summary>
|
||||
/// The base implementation to be used for all operations in the client operation sequence.
|
||||
/// </summary>
|
||||
internal abstract class ClientOperation : IOperation
|
||||
{
|
||||
protected ClientContext Context { get; private set; }
|
||||
|
||||
public abstract event ActionRequiredEventHandler ActionRequired;
|
||||
public abstract event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ClientOperation(ClientContext context)
|
||||
{
|
||||
Context = context;
|
||||
}
|
||||
|
||||
public abstract OperationResult Perform();
|
||||
public abstract OperationResult Revert();
|
||||
}
|
||||
}
|
59
SafeExamBrowser.Client/Operations/ClipboardOperation.cs
Normal file
59
SafeExamBrowser.Client/Operations/ClipboardOperation.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class ClipboardOperation : ClientOperation
|
||||
{
|
||||
private readonly IClipboard clipboard;
|
||||
private readonly ILogger logger;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ClipboardOperation(ClientContext context, IClipboard clipboard, ILogger logger) : base(context)
|
||||
{
|
||||
this.clipboard = clipboard;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
InitializeClipboard();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
FinalizeClipboard();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private void InitializeClipboard()
|
||||
{
|
||||
logger.Info("Initializing clipboard...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeClipboard);
|
||||
clipboard.Initialize(Context.Settings.Security.ClipboardPolicy);
|
||||
}
|
||||
|
||||
private void FinalizeClipboard()
|
||||
{
|
||||
logger.Info("Finalizing clipboard...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeClipboard);
|
||||
clipboard.Terminate();
|
||||
}
|
||||
}
|
||||
}
|
56
SafeExamBrowser.Client/Operations/ConfigurationOperation.cs
Normal file
56
SafeExamBrowser.Client/Operations/ConfigurationOperation.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class ConfigurationOperation : ClientOperation
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly IRuntimeProxy runtime;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ConfigurationOperation(ClientContext context, ILogger logger, IRuntimeProxy runtime) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.runtime = runtime;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing application configuration...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeConfiguration);
|
||||
|
||||
var communication = runtime.GetConfiguration();
|
||||
var configuration = communication.Value.Configuration;
|
||||
|
||||
Context.AppConfig = configuration.AppConfig;
|
||||
Context.SessionId = configuration.SessionId;
|
||||
Context.Settings = configuration.Settings;
|
||||
|
||||
logger.Info("Successfully retrieved the application configuration from the runtime.");
|
||||
logger.Info($" -> Client-ID: {Context.AppConfig.ClientId}");
|
||||
logger.Info($" -> Runtime-ID: {Context.AppConfig.RuntimeId}");
|
||||
logger.Info($" -> Session-ID: {Context.SessionId}");
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
56
SafeExamBrowser.Client/Operations/DisplayMonitorOperation.cs
Normal file
56
SafeExamBrowser.Client/Operations/DisplayMonitorOperation.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Display;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class DisplayMonitorOperation : ClientOperation
|
||||
{
|
||||
private readonly IDisplayMonitor displayMonitor;
|
||||
private readonly ILogger logger;
|
||||
private readonly ITaskbar taskbar;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public DisplayMonitorOperation(ClientContext context, IDisplayMonitor displayMonitor, ILogger logger, ITaskbar taskbar) : base(context)
|
||||
{
|
||||
this.displayMonitor = displayMonitor;
|
||||
this.logger = logger;
|
||||
this.taskbar = taskbar;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing working area...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeWorkingArea);
|
||||
|
||||
displayMonitor.InitializePrimaryDisplay(Context.Settings.UserInterface.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
|
||||
displayMonitor.StartMonitoringDisplayChanges();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
logger.Info("Restoring working area...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_RestoreWorkingArea);
|
||||
|
||||
displayMonitor.StopMonitoringDisplayChanges();
|
||||
displayMonitor.ResetPrimaryDisplay();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Applications.Contracts;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations.Events
|
||||
{
|
||||
internal class ApplicationInitializationFailedEventArgs : ActionRequiredEventArgs
|
||||
{
|
||||
internal string DisplayName { get; }
|
||||
internal string ExecutableName { get; }
|
||||
internal FactoryResult Result { get; }
|
||||
|
||||
internal ApplicationInitializationFailedEventArgs(string displayName, string executableName, FactoryResult result)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
ExecutableName = executableName;
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations.Events
|
||||
{
|
||||
internal class ApplicationNotFoundEventArgs : ActionRequiredEventArgs
|
||||
{
|
||||
internal string CustomPath { get; set; }
|
||||
internal string DisplayName { get; }
|
||||
internal string ExecutableName { get; }
|
||||
internal bool Success { get; set; }
|
||||
|
||||
internal ApplicationNotFoundEventArgs(string displayName, string executableName)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
ExecutableName = executableName;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations.Events
|
||||
{
|
||||
internal class ApplicationTerminationEventArgs : ActionRequiredEventArgs
|
||||
{
|
||||
internal IEnumerable<RunningApplication> RunningApplications { get; }
|
||||
internal bool TerminateProcesses { get; set; }
|
||||
|
||||
internal ApplicationTerminationEventArgs(IEnumerable<RunningApplication> runningApplications)
|
||||
{
|
||||
RunningApplications = runningApplications;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations.Events
|
||||
{
|
||||
internal class ApplicationTerminationFailedEventArgs : ActionRequiredEventArgs
|
||||
{
|
||||
internal IEnumerable<RunningApplication> Applications { get; }
|
||||
|
||||
internal ApplicationTerminationFailedEventArgs(IEnumerable<RunningApplication> applications)
|
||||
{
|
||||
Applications = applications;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Keyboard;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class KeyboardInterceptorOperation : ClientOperation
|
||||
{
|
||||
private IKeyboardInterceptor keyboardInterceptor;
|
||||
private ILogger logger;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public KeyboardInterceptorOperation(ClientContext context, IKeyboardInterceptor keyboardInterceptor, ILogger logger) : base(context)
|
||||
{
|
||||
this.keyboardInterceptor = keyboardInterceptor;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Starting keyboard interception...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StartKeyboardInterception);
|
||||
|
||||
keyboardInterceptor.Start();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
logger.Info("Stopping keyboard interception...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopKeyboardInterception);
|
||||
|
||||
keyboardInterceptor.Stop();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Mouse;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class MouseInterceptorOperation : ClientOperation
|
||||
{
|
||||
private ILogger logger;
|
||||
private IMouseInterceptor mouseInterceptor;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public MouseInterceptorOperation(ClientContext context, ILogger logger, IMouseInterceptor mouseInterceptor) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.mouseInterceptor = mouseInterceptor;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Starting mouse interception...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StartMouseInterception);
|
||||
|
||||
mouseInterceptor.Start();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
logger.Info("Stopping mouse interception...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopMouseInterception);
|
||||
|
||||
mouseInterceptor.Stop();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
93
SafeExamBrowser.Client/Operations/ProctoringOperation.cs
Normal file
93
SafeExamBrowser.Client/Operations/ProctoringOperation.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Proctoring.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class ProctoringOperation : ClientOperation
|
||||
{
|
||||
private readonly IActionCenter actionCenter;
|
||||
private readonly IProctoringController controller;
|
||||
private readonly ILogger logger;
|
||||
private readonly ITaskbar taskbar;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ProctoringOperation(
|
||||
IActionCenter actionCenter,
|
||||
ClientContext context,
|
||||
IProctoringController controller,
|
||||
ILogger logger,
|
||||
ITaskbar taskbar,
|
||||
IUserInterfaceFactory uiFactory) : base(context)
|
||||
{
|
||||
this.actionCenter = actionCenter;
|
||||
this.controller = controller;
|
||||
this.logger = logger;
|
||||
this.taskbar = taskbar;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
if (Context.Settings.Proctoring.Enabled)
|
||||
{
|
||||
logger.Info("Initializing proctoring...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeProctoring);
|
||||
|
||||
controller.Initialize(Context.Settings.Proctoring);
|
||||
|
||||
if (Context.Settings.SessionMode == SessionMode.Server && Context.Settings.Proctoring.ShowRaiseHandNotification)
|
||||
{
|
||||
actionCenter.AddNotificationControl(uiFactory.CreateRaiseHandControl(controller, Location.ActionCenter, Context.Settings.Proctoring));
|
||||
taskbar.AddNotificationControl(uiFactory.CreateRaiseHandControl(controller, Location.Taskbar, Context.Settings.Proctoring));
|
||||
}
|
||||
|
||||
foreach (var notification in controller.Notifications)
|
||||
{
|
||||
actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(notification, Location.ActionCenter));
|
||||
|
||||
if (Context.Settings.Proctoring.ShowTaskbarNotification)
|
||||
{
|
||||
taskbar.AddNotificationControl(uiFactory.CreateNotificationControl(notification, Location.Taskbar));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
if (Context.Settings.Proctoring.Enabled)
|
||||
{
|
||||
logger.Info("Terminating proctoring...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateProctoring);
|
||||
|
||||
controller.Terminate();
|
||||
|
||||
foreach (var notification in controller.Notifications)
|
||||
{
|
||||
notification.Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class RuntimeConnectionOperation : ClientOperation
|
||||
{
|
||||
private ILogger logger;
|
||||
private IRuntimeProxy runtime;
|
||||
private Guid token;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public RuntimeConnectionOperation(ClientContext context, ILogger logger, IRuntimeProxy runtime, Guid token) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.runtime = runtime;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing runtime connection...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeRuntimeConnection);
|
||||
|
||||
var connected = runtime.Connect(token);
|
||||
|
||||
if (connected)
|
||||
{
|
||||
logger.Info("Successfully connected to the runtime.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to connect to the runtime. Aborting startup...");
|
||||
}
|
||||
|
||||
return connected ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
logger.Info("Closing runtime connection...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_CloseRuntimeConnection);
|
||||
|
||||
if (runtime.IsConnected)
|
||||
{
|
||||
var success = runtime.Disconnect();
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Info("Successfully disconnected from the runtime.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to disconnect from the runtime!");
|
||||
}
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
64
SafeExamBrowser.Client/Operations/ServerOperation.cs
Normal file
64
SafeExamBrowser.Client/Operations/ServerOperation.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class ServerOperation : ClientOperation
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly IServerProxy server;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ServerOperation(ClientContext context, ILogger logger, IServerProxy server) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
if (Context.Settings.SessionMode == SessionMode.Server)
|
||||
{
|
||||
logger.Info("Initializing server...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServer);
|
||||
|
||||
server.Initialize(
|
||||
Context.AppConfig.ServerApi,
|
||||
Context.AppConfig.ServerConnectionToken,
|
||||
Context.AppConfig.ServerExamId,
|
||||
Context.AppConfig.ServerOauth2Token,
|
||||
Context.Settings.Server);
|
||||
server.StartConnectivity();
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
if (Context.Settings?.SessionMode == SessionMode.Server)
|
||||
{
|
||||
logger.Info("Finalizing server...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServer);
|
||||
|
||||
server.StopConnectivity();
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
371
SafeExamBrowser.Client/Operations/ShellOperation.cs
Normal file
371
SafeExamBrowser.Client/Operations/ShellOperation.cs
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using SafeExamBrowser.Core.Contracts.Notifications;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Audio;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Network;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class ShellOperation : ClientOperation
|
||||
{
|
||||
private readonly IActionCenter actionCenter;
|
||||
private readonly IAudio audio;
|
||||
private readonly INotification aboutNotification;
|
||||
private readonly IKeyboard keyboard;
|
||||
private readonly ILogger logger;
|
||||
private readonly INotification logNotification;
|
||||
private readonly INativeMethods nativeMethods;
|
||||
private readonly INetworkAdapter networkAdapter;
|
||||
private readonly IPowerSupply powerSupply;
|
||||
private readonly ISystemInfo systemInfo;
|
||||
private readonly ITaskbar taskbar;
|
||||
private readonly ITaskview taskview;
|
||||
private readonly IText text;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ShellOperation(
|
||||
IActionCenter actionCenter,
|
||||
IAudio audio,
|
||||
INotification aboutNotification,
|
||||
ClientContext context,
|
||||
IKeyboard keyboard,
|
||||
ILogger logger,
|
||||
INotification logNotification,
|
||||
INativeMethods nativeMethods,
|
||||
INetworkAdapter networkAdapter,
|
||||
IPowerSupply powerSupply,
|
||||
ISystemInfo systemInfo,
|
||||
ITaskbar taskbar,
|
||||
ITaskview taskview,
|
||||
IText text,
|
||||
IUserInterfaceFactory uiFactory) : base(context)
|
||||
{
|
||||
this.aboutNotification = aboutNotification;
|
||||
this.actionCenter = actionCenter;
|
||||
this.audio = audio;
|
||||
this.keyboard = keyboard;
|
||||
this.logger = logger;
|
||||
this.logNotification = logNotification;
|
||||
this.nativeMethods = nativeMethods;
|
||||
this.networkAdapter = networkAdapter;
|
||||
this.powerSupply = powerSupply;
|
||||
this.systemInfo = systemInfo;
|
||||
this.text = text;
|
||||
this.taskbar = taskbar;
|
||||
this.taskview = taskview;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing shell...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeShell);
|
||||
|
||||
InitializeSystemComponents();
|
||||
InitializeActionCenter();
|
||||
InitializeTaskbar();
|
||||
InitializeTaskview();
|
||||
InitializeActivators();
|
||||
InitializeAlwaysOnState();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
logger.Info("Terminating shell...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateShell);
|
||||
|
||||
TerminateActivators();
|
||||
TerminateNotifications();
|
||||
TerminateSystemComponents();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private void InitializeActivators()
|
||||
{
|
||||
foreach (var activator in Context.Activators)
|
||||
{
|
||||
if (Context.Settings.UserInterface.ActionCenter.EnableActionCenter && activator is IActionCenterActivator actionCenterActivator)
|
||||
{
|
||||
actionCenter.Register(actionCenterActivator);
|
||||
actionCenterActivator.Start();
|
||||
}
|
||||
|
||||
if (Context.Settings.Keyboard.AllowAltTab && activator is ITaskviewActivator taskViewActivator)
|
||||
{
|
||||
taskview.Register(taskViewActivator);
|
||||
taskViewActivator.Start();
|
||||
}
|
||||
|
||||
if (Context.Settings.Security.AllowTermination && activator is ITerminationActivator terminationActivator)
|
||||
{
|
||||
terminationActivator.Start();
|
||||
}
|
||||
|
||||
if (Context.Settings.UserInterface.Taskbar.EnableTaskbar && activator is ITaskbarActivator taskbarActivator)
|
||||
{
|
||||
taskbar.Register(taskbarActivator);
|
||||
taskbarActivator.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeActionCenter()
|
||||
{
|
||||
if (Context.Settings.UserInterface.ActionCenter.EnableActionCenter)
|
||||
{
|
||||
logger.Info("Initializing action center...");
|
||||
actionCenter.InitializeText(text);
|
||||
|
||||
InitializeApplicationsFor(Location.ActionCenter);
|
||||
InitializeAboutNotificationForActionCenter();
|
||||
InitializeAudioForActionCenter();
|
||||
InitializeClockForActionCenter();
|
||||
InitializeLogNotificationForActionCenter();
|
||||
InitializeKeyboardLayoutForActionCenter();
|
||||
InitializeNetworkForActionCenter();
|
||||
InitializePowerSupplyForActionCenter();
|
||||
InitializeQuitButtonForActionCenter();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("Action center is disabled, skipping initialization.");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAlwaysOnState()
|
||||
{
|
||||
var display = Context.Settings.Display.AlwaysOn;
|
||||
var system = Context.Settings.System.AlwaysOn;
|
||||
|
||||
nativeMethods.SetAlwaysOnState(display, system);
|
||||
|
||||
logger.Info($"Display(s) will {(display ? "be always on" : "use the operating system configuration and may turn off")}.");
|
||||
logger.Info($"System will {(system ? "be always on" : "use the operating system configuration and may enter sleep mode or standby")}.");
|
||||
}
|
||||
|
||||
private void InitializeTaskbar()
|
||||
{
|
||||
if (Context.Settings.UserInterface.Taskbar.EnableTaskbar)
|
||||
{
|
||||
logger.Info("Initializing taskbar...");
|
||||
taskbar.InitializeText(text);
|
||||
|
||||
InitializeApplicationsFor(Location.Taskbar);
|
||||
InitializeAboutNotificationForTaskbar();
|
||||
InitializeLogNotificationForTaskbar();
|
||||
InitializePowerSupplyForTaskbar();
|
||||
InitializeNetworkForTaskbar();
|
||||
InitializeAudioForTaskbar();
|
||||
InitializeKeyboardLayoutForTaskbar();
|
||||
InitializeClockForTaskbar();
|
||||
InitializeQuitButtonForTaskbar();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("Taskbar is disabled, skipping initialization.");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeTaskview()
|
||||
{
|
||||
logger.Info("Initializing task view...");
|
||||
|
||||
foreach (var application in Context.Applications)
|
||||
{
|
||||
taskview.Add(application);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeApplicationsFor(Location location)
|
||||
{
|
||||
foreach (var application in Context.Applications)
|
||||
{
|
||||
var settings = Context.Settings.Applications.Whitelist.First(a => a.Id == application.Id);
|
||||
|
||||
if (settings.ShowInShell)
|
||||
{
|
||||
var control = uiFactory.CreateApplicationControl(application, location);
|
||||
|
||||
switch (location)
|
||||
{
|
||||
case Location.ActionCenter:
|
||||
actionCenter.AddApplicationControl(control);
|
||||
break;
|
||||
case Location.Taskbar:
|
||||
taskbar.AddApplicationControl(control);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeSystemComponents()
|
||||
{
|
||||
audio.Initialize();
|
||||
keyboard.Initialize();
|
||||
networkAdapter.Initialize();
|
||||
powerSupply.Initialize();
|
||||
}
|
||||
|
||||
private void InitializeAboutNotificationForActionCenter()
|
||||
{
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowApplicationInfo)
|
||||
{
|
||||
actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(aboutNotification, Location.ActionCenter));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAboutNotificationForTaskbar()
|
||||
{
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowApplicationInfo)
|
||||
{
|
||||
taskbar.AddNotificationControl(uiFactory.CreateNotificationControl(aboutNotification, Location.Taskbar));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAudioForActionCenter()
|
||||
{
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowAudio)
|
||||
{
|
||||
actionCenter.AddSystemControl(uiFactory.CreateAudioControl(audio, Location.ActionCenter));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAudioForTaskbar()
|
||||
{
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowAudio)
|
||||
{
|
||||
taskbar.AddSystemControl(uiFactory.CreateAudioControl(audio, Location.Taskbar));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeClockForActionCenter()
|
||||
{
|
||||
actionCenter.ShowClock = Context.Settings.UserInterface.ActionCenter.ShowClock;
|
||||
}
|
||||
|
||||
private void InitializeClockForTaskbar()
|
||||
{
|
||||
taskbar.ShowClock = Context.Settings.UserInterface.Taskbar.ShowClock;
|
||||
}
|
||||
|
||||
private void InitializeLogNotificationForActionCenter()
|
||||
{
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowApplicationLog)
|
||||
{
|
||||
actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(logNotification, Location.ActionCenter));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeLogNotificationForTaskbar()
|
||||
{
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowApplicationLog)
|
||||
{
|
||||
taskbar.AddNotificationControl(uiFactory.CreateNotificationControl(logNotification, Location.Taskbar));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeKeyboardLayoutForActionCenter()
|
||||
{
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowKeyboardLayout)
|
||||
{
|
||||
actionCenter.AddSystemControl(uiFactory.CreateKeyboardLayoutControl(keyboard, Location.ActionCenter));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeKeyboardLayoutForTaskbar()
|
||||
{
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowKeyboardLayout)
|
||||
{
|
||||
taskbar.AddSystemControl(uiFactory.CreateKeyboardLayoutControl(keyboard, Location.Taskbar));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializePowerSupplyForActionCenter()
|
||||
{
|
||||
if (systemInfo.HasBattery)
|
||||
{
|
||||
actionCenter.AddSystemControl(uiFactory.CreatePowerSupplyControl(powerSupply, Location.ActionCenter));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializePowerSupplyForTaskbar()
|
||||
{
|
||||
if (systemInfo.HasBattery)
|
||||
{
|
||||
taskbar.AddSystemControl(uiFactory.CreatePowerSupplyControl(powerSupply, Location.Taskbar));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeQuitButtonForActionCenter()
|
||||
{
|
||||
actionCenter.ShowQuitButton = Context.Settings.Security.AllowTermination;
|
||||
}
|
||||
|
||||
private void InitializeQuitButtonForTaskbar()
|
||||
{
|
||||
taskbar.ShowQuitButton = Context.Settings.Security.AllowTermination;
|
||||
}
|
||||
|
||||
private void InitializeNetworkForActionCenter()
|
||||
{
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowNetwork)
|
||||
{
|
||||
actionCenter.AddSystemControl(uiFactory.CreateNetworkControl(networkAdapter, Location.ActionCenter));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeNetworkForTaskbar()
|
||||
{
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowNetwork)
|
||||
{
|
||||
taskbar.AddSystemControl(uiFactory.CreateNetworkControl(networkAdapter, Location.Taskbar));
|
||||
}
|
||||
}
|
||||
|
||||
private void TerminateActivators()
|
||||
{
|
||||
foreach (var activator in Context.Activators)
|
||||
{
|
||||
activator.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void TerminateNotifications()
|
||||
{
|
||||
aboutNotification.Terminate();
|
||||
logNotification.Terminate();
|
||||
}
|
||||
|
||||
private void TerminateSystemComponents()
|
||||
{
|
||||
audio.Terminate();
|
||||
keyboard.Terminate();
|
||||
networkAdapter.Terminate();
|
||||
powerSupply.Terminate();
|
||||
}
|
||||
}
|
||||
}
|
56
SafeExamBrowser.Client/Properties/AssemblyInfo.cs
Normal file
56
SafeExamBrowser.Client/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("SafeExamBrowser.Client")]
|
||||
[assembly: AssemblyDescription("Safe Exam Browser")]
|
||||
[assembly: AssemblyCompany("ETH Zürich")]
|
||||
[assembly: AssemblyProduct("SafeExamBrowser.Client")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Required for mocking internal contracts with Moq
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("SafeExamBrowser.Client.UnitTests")]
|
||||
|
||||
//In order to begin building localizable applications, set
|
||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||
//inside a <PropertyGroup>. For example, if you are using US english
|
||||
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||
//the line below to match the UICulture setting in the project file.
|
||||
|
||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("3.8.0.742")]
|
||||
[assembly: AssemblyFileVersion("3.8.0.742")]
|
||||
[assembly: AssemblyInformationalVersion("3.8.0.742")]
|
63
SafeExamBrowser.Client/Properties/Resources.Designer.cs
generated
Normal file
63
SafeExamBrowser.Client/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,63 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace SafeExamBrowser.Client.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SafeExamBrowser.Client.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
117
SafeExamBrowser.Client/Properties/Resources.resx
Normal file
117
SafeExamBrowser.Client/Properties/Resources.resx
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
26
SafeExamBrowser.Client/Properties/Settings.Designer.cs
generated
Normal file
26
SafeExamBrowser.Client/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace SafeExamBrowser.Client.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.6.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
SafeExamBrowser.Client/Properties/Settings.settings
Normal file
7
SafeExamBrowser.Client/Properties/Settings.settings
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
271
SafeExamBrowser.Client/SafeExamBrowser.Client.csproj
Normal file
271
SafeExamBrowser.Client/SafeExamBrowser.Client.csproj
Normal file
@@ -0,0 +1,271 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{7CC5A895-E0D3-4E43-9B39-CCEC05A5A6A7}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>SafeExamBrowser.Client</RootNamespace>
|
||||
<AssemblyName>SafeExamBrowser.Client</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>SafeExamBrowser.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="App.cs" />
|
||||
<Compile Include="ClientContext.cs" />
|
||||
<Compile Include="ClientController.cs" />
|
||||
<Compile Include="Contracts\ICoordinator.cs" />
|
||||
<Compile Include="Coordinator.cs" />
|
||||
<Compile Include="Operations\ClientHostDisconnectionOperation.cs" />
|
||||
<Compile Include="Operations\ClientOperation.cs" />
|
||||
<Compile Include="Operations\ConfigurationOperation.cs" />
|
||||
<Compile Include="Operations\Events\ApplicationNotFoundEventArgs.cs" />
|
||||
<Compile Include="Operations\Events\ApplicationInitializationFailedEventArgs.cs" />
|
||||
<Compile Include="Operations\Events\ApplicationTerminationEventArgs.cs" />
|
||||
<Compile Include="Operations\Events\ApplicationTerminationFailedEventArgs.cs" />
|
||||
<Compile Include="Operations\ProctoringOperation.cs" />
|
||||
<Compile Include="Operations\RuntimeConnectionOperation.cs" />
|
||||
<Compile Include="Communication\ClientHost.cs" />
|
||||
<Compile Include="CompositionRoot.cs" />
|
||||
<Compile Include="Notifications\AboutNotification.cs" />
|
||||
<Compile Include="Notifications\LogNotification.cs" />
|
||||
<Compile Include="Operations\BrowserOperation.cs" />
|
||||
<Compile Include="Operations\ClipboardOperation.cs" />
|
||||
<Compile Include="Operations\DisplayMonitorOperation.cs" />
|
||||
<Compile Include="Operations\KeyboardInterceptorOperation.cs" />
|
||||
<Compile Include="Operations\MouseInterceptorOperation.cs" />
|
||||
<Compile Include="Operations\ApplicationOperation.cs" />
|
||||
<Compile Include="Operations\ServerOperation.cs" />
|
||||
<Compile Include="Operations\ShellOperation.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="app.manifest" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Applications.Contracts\SafeExamBrowser.Applications.Contracts.csproj">
|
||||
<Project>{ac77745d-3b41-43e2-8e84-d40e5a4ee77f}</Project>
|
||||
<Name>SafeExamBrowser.Applications.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Applications\SafeExamBrowser.Applications.csproj">
|
||||
<Project>{a113e68f-1209-4689-981a-15c554b2df4e}</Project>
|
||||
<Name>SafeExamBrowser.Applications</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Browser.Contracts\SafeExamBrowser.Browser.Contracts.csproj">
|
||||
<Project>{5fb5273d-277c-41dd-8593-a25ce1aff2e9}</Project>
|
||||
<Name>SafeExamBrowser.Browser.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Browser\SafeExamBrowser.Browser.csproj">
|
||||
<Project>{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}</Project>
|
||||
<Name>SafeExamBrowser.Browser</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Communication.Contracts\SafeExamBrowser.Communication.Contracts.csproj">
|
||||
<Project>{0cd2c5fe-711a-4c32-afe0-bb804fe8b220}</Project>
|
||||
<Name>SafeExamBrowser.Communication.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Communication\SafeExamBrowser.Communication.csproj">
|
||||
<Project>{c9416a62-0623-4d38-96aa-92516b32f02f}</Project>
|
||||
<Name>SafeExamBrowser.Communication</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Configuration.Contracts\SafeExamBrowser.Configuration.Contracts.csproj">
|
||||
<Project>{7d74555e-63e1-4c46-bd0a-8580552368c8}</Project>
|
||||
<Name>SafeExamBrowser.Configuration.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Configuration\SafeExamBrowser.Configuration.csproj">
|
||||
<Project>{C388C4DD-A159-457D-AF92-89F7AD185109}</Project>
|
||||
<Name>SafeExamBrowser.Configuration</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Core.Contracts\SafeExamBrowser.Core.Contracts.csproj">
|
||||
<Project>{fe0e1224-b447-4b14-81e7-ed7d84822aa0}</Project>
|
||||
<Name>SafeExamBrowser.Core.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Core\SafeExamBrowser.Core.csproj">
|
||||
<Project>{3D6FDBB6-A4AF-4626-BB2B-BF329D44F9CC}</Project>
|
||||
<Name>SafeExamBrowser.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.I18n.Contracts\SafeExamBrowser.I18n.Contracts.csproj">
|
||||
<Project>{1858ddf3-bc2a-4bff-b663-4ce2ffeb8b7d}</Project>
|
||||
<Name>SafeExamBrowser.I18n.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.I18n\SafeExamBrowser.I18n.csproj">
|
||||
<Project>{10c62628-8e6a-45aa-9d97-339b119ad21d}</Project>
|
||||
<Name>SafeExamBrowser.I18n</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
|
||||
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
|
||||
<Name>SafeExamBrowser.Logging.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Logging\SafeExamBrowser.Logging.csproj">
|
||||
<Project>{e107026c-2011-4552-a7d8-3a0d37881df6}</Project>
|
||||
<Name>SafeExamBrowser.Logging</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Monitoring.Contracts\SafeExamBrowser.Monitoring.Contracts.csproj">
|
||||
<Project>{6d563a30-366d-4c35-815b-2c9e6872278b}</Project>
|
||||
<Name>SafeExamBrowser.Monitoring.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Monitoring\SafeExamBrowser.Monitoring.csproj">
|
||||
<Project>{EF563531-4EB5-44B9-A5EC-D6D6F204469B}</Project>
|
||||
<Name>SafeExamBrowser.Monitoring</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Proctoring.Contracts\SafeExamBrowser.Proctoring.Contracts.csproj">
|
||||
<Project>{8e52bd1c-0540-4f16-b181-6665d43f7a7b}</Project>
|
||||
<Name>SafeExamBrowser.Proctoring.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Proctoring\SafeExamBrowser.Proctoring.csproj">
|
||||
<Project>{3f1f262e-a07c-4513-83c6-d7ef2f203ebf}</Project>
|
||||
<Name>SafeExamBrowser.Proctoring</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Server.Contracts\SafeExamBrowser.Server.Contracts.csproj">
|
||||
<Project>{db701e6f-bddc-4cec-b662-335a9dc11809}</Project>
|
||||
<Name>SafeExamBrowser.Server.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Server\SafeExamBrowser.Server.csproj">
|
||||
<Project>{46edbde0-58b4-4725-9783-0c55c3d49c0c}</Project>
|
||||
<Name>SafeExamBrowser.Server</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
|
||||
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
|
||||
<Name>SafeExamBrowser.Settings</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.SystemComponents.Contracts\SafeExamBrowser.SystemComponents.Contracts.csproj">
|
||||
<Project>{903129c6-e236-493b-9ad6-c6a57f647a3a}</Project>
|
||||
<Name>SafeExamBrowser.SystemComponents.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.SystemComponents\SafeExamBrowser.SystemComponents.csproj">
|
||||
<Project>{ACEE2EF1-14D2-4B52-8994-5C053055BB51}</Project>
|
||||
<Name>SafeExamBrowser.SystemComponents</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Contracts\SafeExamBrowser.UserInterface.Contracts.csproj">
|
||||
<Project>{c7889e97-6ff6-4a58-b7cb-521ed276b316}</Project>
|
||||
<Name>SafeExamBrowser.UserInterface.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Desktop\SafeExamBrowser.UserInterface.Desktop.csproj">
|
||||
<Project>{A502DF54-7169-4647-94BD-18B192924866}</Project>
|
||||
<Name>SafeExamBrowser.UserInterface.Desktop</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Mobile\SafeExamBrowser.UserInterface.Mobile.csproj">
|
||||
<Project>{89bc24dd-ff31-496e-9816-a160b686a3d4}</Project>
|
||||
<Name>SafeExamBrowser.UserInterface.Mobile</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Shared\SafeExamBrowser.UserInterface.Shared.csproj">
|
||||
<Project>{38525928-87ba-4f8c-8010-4eb97bfaae13}</Project>
|
||||
<Name>SafeExamBrowser.UserInterface.Shared</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.WindowsApi.Contracts\SafeExamBrowser.WindowsApi.Contracts.csproj">
|
||||
<Project>{7016f080-9aa5-41b2-a225-385ad877c171}</Project>
|
||||
<Name>SafeExamBrowser.WindowsApi.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.WindowsApi\SafeExamBrowser.WindowsApi.csproj">
|
||||
<Project>{73724659-4150-4792-A94E-42F5F3C1B696}</Project>
|
||||
<Name>SafeExamBrowser.WindowsApi</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="SafeExamBrowser.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>robocopy "$(SolutionDir)SafeExamBrowser.Browser\bin\$(PlatformName)\$(ConfigurationName)" "$(TargetDir)\" /e /np
|
||||
|
||||
IF %25ERRORLEVEL%25 LSS 8 (
|
||||
robocopy "$(TargetDir)\" "$(SolutionDir)SafeExamBrowser.Runtime\bin\$(PlatformName)\$(ConfigurationName)" /e /np
|
||||
|
||||
IF %25ERRORLEVEL%25 LSS 8 (
|
||||
EXIT 0
|
||||
)
|
||||
)
|
||||
|
||||
EXIT 1</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Target Name="AfterClean" AfterTargets="Clean">
|
||||
<RemoveDir Directories="$(TargetDir)" />
|
||||
</Target>
|
||||
</Project>
|
BIN
SafeExamBrowser.Client/SafeExamBrowser.ico
Normal file
BIN
SafeExamBrowser.Client/SafeExamBrowser.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 361 KiB |
34
SafeExamBrowser.Client/app.manifest
Normal file
34
SafeExamBrowser.Client/app.manifest
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- A list of the Windows versions that this application has been tested on and is
|
||||
is designed to work with. Uncomment the appropriate elements and Windows will
|
||||
automatically selected the most compatible environment. -->
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||
<!-- Windows 10 and Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||
<!-- IMPORTANT: This dependency is required by CEF (e.g. to render tooltips) -->
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
</assembly>
|
Reference in New Issue
Block a user