diff --git a/SafeExamBrowser.Client/App.cs b/SafeExamBrowser.Client/App.cs index c4cf75e4..0b67fead 100644 --- a/SafeExamBrowser.Client/App.cs +++ b/SafeExamBrowser.Client/App.cs @@ -7,7 +7,6 @@ */ using System; -using System.ComponentModel; using System.Threading; using System.Windows; @@ -56,14 +55,16 @@ namespace SafeExamBrowser.Client { base.OnStartup(e); + ShutdownMode = ShutdownMode.OnMainWindowClose; + instances.BuildObjectGraph(); + instances.LogStartupInformation(); var success = instances.ClientController.TryStart(); if (success) { MainWindow = instances.Taskbar; - MainWindow.Closing += MainWindow_Closing; MainWindow.Show(); } else @@ -72,10 +73,12 @@ namespace SafeExamBrowser.Client } } - private void MainWindow_Closing(object sender, CancelEventArgs e) + protected override void OnExit(ExitEventArgs e) { - MainWindow?.Hide(); instances.ClientController.Terminate(); + instances.LogShutdownInformation(); + + base.OnExit(e); } } } diff --git a/SafeExamBrowser.Client/Communication/ClientHost.cs b/SafeExamBrowser.Client/Communication/ClientHost.cs index e0fa0dc9..c89aeb7f 100644 --- a/SafeExamBrowser.Client/Communication/ClientHost.cs +++ b/SafeExamBrowser.Client/Communication/ClientHost.cs @@ -17,10 +17,13 @@ namespace SafeExamBrowser.Client.Communication { internal class ClientHost : BaseHost, IClientHost { + private int processId; + public Guid StartupToken { private get; set; } - public ClientHost(string address, ILogger logger) : base(address, logger) + public ClientHost(string address, ILogger logger, int processId) : base(address, logger) { + this.processId = processId; } protected override bool OnConnect(Guid? token) @@ -36,13 +39,19 @@ namespace SafeExamBrowser.Client.Communication protected override Response OnReceive(Message message) { // TODO - return null; + + return new SimpleResponse(SimpleResponsePurport.UnknownMessage); } - protected override Response OnReceive(MessagePurport message) + protected override Response OnReceive(SimpleMessagePurport message) { - // TODO - return null; + switch (message) + { + case SimpleMessagePurport.Authenticate: + return new AuthenticationResponse { ProcessId = processId }; + } + + return new SimpleResponse(SimpleResponsePurport.UnknownMessage); } } } diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index aec4bb97..70f11bec 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using SafeExamBrowser.Browser; using SafeExamBrowser.Client.Behaviour; using SafeExamBrowser.Client.Behaviour.Operations; @@ -37,6 +38,10 @@ namespace SafeExamBrowser.Client { internal class CompositionRoot { + private string logFilePath; + private string runtimeHostUri; + private Guid startupToken; + private ClientConfiguration configuration; private ILogger logger; private INativeMethods nativeMethods; @@ -49,21 +54,19 @@ namespace SafeExamBrowser.Client internal void BuildObjectGraph() { - var args = Environment.GetCommandLineArgs(); - - Validate(args); + ValidateCommandLineArguments(); configuration = new ClientConfiguration(); logger = new Logger(); nativeMethods = new NativeMethods(); systemInfo = new SystemInfo(); - InitializeLogging(args[1]); + InitializeLogging(); text = new Text(logger); uiFactory = new UserInterfaceFactory(text); - var runtimeProxy = new RuntimeProxy(args[2], new ModuleLogger(logger, typeof(RuntimeProxy))); + var runtimeProxy = new RuntimeProxy(runtimeHostUri, new ModuleLogger(logger, typeof(RuntimeProxy))); var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods); var processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods); var windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods); @@ -73,25 +76,38 @@ namespace SafeExamBrowser.Client var operations = new Queue(); operations.Enqueue(new I18nOperation(logger, text)); - operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, Guid.Parse(args[3]))); + operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, startupToken)); operations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeProxy)); operations.Enqueue(new DelayedInitializationOperation(BuildCommunicationHostOperation)); - operations.Enqueue(new DelayedInitializationOperation(BuildKeyboardInterceptorOperation)); - operations.Enqueue(new WindowMonitorOperation(logger, windowMonitor)); - operations.Enqueue(new ProcessMonitorOperation(logger, processMonitor)); + // TODO + //operations.Enqueue(new DelayedInitializationOperation(BuildKeyboardInterceptorOperation)); + //operations.Enqueue(new WindowMonitorOperation(logger, windowMonitor)); + //operations.Enqueue(new ProcessMonitorOperation(logger, processMonitor)); operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar)); operations.Enqueue(new DelayedInitializationOperation(BuildTaskbarOperation)); operations.Enqueue(new DelayedInitializationOperation(BuildBrowserOperation)); operations.Enqueue(new ClipboardOperation(logger, nativeMethods)); - operations.Enqueue(new DelayedInitializationOperation(BuildMouseInterceptorOperation)); + //operations.Enqueue(new DelayedInitializationOperation(BuildMouseInterceptorOperation)); var sequence = new OperationSequence(logger, operations); ClientController = new ClientController(displayMonitor, logger, sequence, processMonitor, runtimeProxy, Taskbar, windowMonitor); } - private void Validate(string[] args) + internal void LogStartupInformation() { + logger.Log($"# New client instance started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); + logger.Log(string.Empty); + } + + internal void LogShutdownInformation() + { + logger?.Log($"# Client instance terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); + } + + private void ValidateCommandLineArguments() + { + var args = Environment.GetCommandLineArgs(); var hasFour = args?.Length == 4; if (hasFour) @@ -102,16 +118,20 @@ namespace SafeExamBrowser.Client if (hasLogfilePath && hasHostUri && hasToken) { + logFilePath = args[1]; + runtimeHostUri = args[2]; + startupToken = Guid.Parse(args[3]); + return; } } - throw new ArgumentException("Invalid parameters! Required: SafeExamBrowser.Client.exe "); + throw new ArgumentException("Invalid arguments! Required: SafeExamBrowser.Client.exe "); } - private void InitializeLogging(string filePath) + private void InitializeLogging() { - var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), filePath); + var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), logFilePath); logFileWriter.Initialize(); logger.Subscribe(logFileWriter); @@ -128,9 +148,12 @@ namespace SafeExamBrowser.Client private IOperation BuildCommunicationHostOperation() { - var host = new ClientHost(configuration.RuntimeInfo.ClientAddress, new ModuleLogger(logger, typeof(ClientHost))); + var processId = Process.GetCurrentProcess().Id; + var host = new ClientHost(configuration.RuntimeInfo.ClientAddress, new ModuleLogger(logger, typeof(ClientHost)), processId); var operation = new CommunicationOperation(host, logger); + host.StartupToken = startupToken; + return operation; } diff --git a/SafeExamBrowser.Configuration/ConfigurationRepository.cs b/SafeExamBrowser.Configuration/ConfigurationRepository.cs index c2ce726b..194a3552 100644 --- a/SafeExamBrowser.Configuration/ConfigurationRepository.cs +++ b/SafeExamBrowser.Configuration/ConfigurationRepository.cs @@ -73,6 +73,9 @@ namespace SafeExamBrowser.Configuration }; settings.Browser.StartUrl = "https://www.duckduckgo.com"; + settings.Taskbar.AllowApplicationLog = true; + settings.Taskbar.AllowKeyboardLayout = true; + settings.Taskbar.AllowWirelessNetwork = true; CurrentSettings = settings; diff --git a/SafeExamBrowser.Contracts/Communication/ICommunication.cs b/SafeExamBrowser.Contracts/Communication/ICommunication.cs index d2f1d9b6..14d1816f 100644 --- a/SafeExamBrowser.Contracts/Communication/ICommunication.cs +++ b/SafeExamBrowser.Contracts/Communication/ICommunication.cs @@ -19,6 +19,7 @@ namespace SafeExamBrowser.Contracts.Communication [ServiceKnownType(typeof(AuthenticationResponse))] [ServiceKnownType(typeof(ConfigurationResponse))] [ServiceKnownType(typeof(ClientConfiguration))] + [ServiceKnownType(typeof(SimpleResponse))] public interface ICommunication { /// diff --git a/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs b/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs index 2207d2e4..7b05f063 100644 --- a/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs +++ b/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs @@ -20,11 +20,13 @@ namespace SafeExamBrowser.Contracts.Communication /// /// Starts the host and opens it for communication. /// + /// If the host fails to start. void Start(); /// /// Closes and terminates the host. /// + /// If the host fails to terminate. void Stop(); } } diff --git a/SafeExamBrowser.Contracts/Communication/Messages/Message.cs b/SafeExamBrowser.Contracts/Communication/Messages/Message.cs index 6f9769e6..54f8700e 100644 --- a/SafeExamBrowser.Contracts/Communication/Messages/Message.cs +++ b/SafeExamBrowser.Contracts/Communication/Messages/Message.cs @@ -17,5 +17,10 @@ namespace SafeExamBrowser.Contracts.Communication.Messages /// The communication token needed for authentication. /// public Guid CommunicationToken { get; set; } + + public override string ToString() + { + return GetType().Name; + } } } diff --git a/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessage.cs b/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessage.cs index b46fb3de..9a07c93e 100644 --- a/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessage.cs +++ b/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessage.cs @@ -16,6 +16,16 @@ namespace SafeExamBrowser.Contracts.Communication.Messages /// /// The purport of the message. /// - public MessagePurport Purport { get; set; } + public SimpleMessagePurport Purport { get; set; } + + public SimpleMessage(SimpleMessagePurport purport) + { + Purport = purport; + } + + public override string ToString() + { + return $"{base.ToString()} -> {Purport}"; + } } } diff --git a/SafeExamBrowser.Contracts/Communication/Messages/MessagePurport.cs b/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessagePurport.cs similarity index 95% rename from SafeExamBrowser.Contracts/Communication/Messages/MessagePurport.cs rename to SafeExamBrowser.Contracts/Communication/Messages/SimpleMessagePurport.cs index 858c9f5b..e4c6f4a6 100644 --- a/SafeExamBrowser.Contracts/Communication/Messages/MessagePurport.cs +++ b/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessagePurport.cs @@ -11,7 +11,7 @@ using System; namespace SafeExamBrowser.Contracts.Communication.Messages { [Serializable] - public enum MessagePurport + public enum SimpleMessagePurport { /// /// Requests an interlocutor to submit data for authentication. diff --git a/SafeExamBrowser.Contracts/Communication/Responses/AuthenticationResponse.cs b/SafeExamBrowser.Contracts/Communication/Responses/AuthenticationResponse.cs index 51e20020..2a3115dc 100644 --- a/SafeExamBrowser.Contracts/Communication/Responses/AuthenticationResponse.cs +++ b/SafeExamBrowser.Contracts/Communication/Responses/AuthenticationResponse.cs @@ -16,6 +16,6 @@ namespace SafeExamBrowser.Contracts.Communication.Responses /// /// The process identifier used for authentication. /// - public int ProcessId { get; } + public int ProcessId { get; set; } } } diff --git a/SafeExamBrowser.Contracts/Communication/Responses/Response.cs b/SafeExamBrowser.Contracts/Communication/Responses/Response.cs index 8774e824..ba3f061d 100644 --- a/SafeExamBrowser.Contracts/Communication/Responses/Response.cs +++ b/SafeExamBrowser.Contracts/Communication/Responses/Response.cs @@ -13,5 +13,9 @@ namespace SafeExamBrowser.Contracts.Communication.Responses [Serializable] public class Response { + public override string ToString() + { + return GetType().Name; + } } } diff --git a/SafeExamBrowser.Contracts/Communication/Responses/SimpleResponse.cs b/SafeExamBrowser.Contracts/Communication/Responses/SimpleResponse.cs new file mode 100644 index 00000000..cc87c733 --- /dev/null +++ b/SafeExamBrowser.Contracts/Communication/Responses/SimpleResponse.cs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) + * + * 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; + +namespace SafeExamBrowser.Contracts.Communication.Responses +{ + [Serializable] + public class SimpleResponse : Response + { + /// + /// The purport of the response. + /// + public SimpleResponsePurport Purport { get; set; } + + public SimpleResponse(SimpleResponsePurport purport) + { + Purport = purport; + } + + public override string ToString() + { + return $"{base.ToString()} -> {Purport}"; + } + } +} diff --git a/SafeExamBrowser.Contracts/Communication/Responses/SimpleResponsePurport.cs b/SafeExamBrowser.Contracts/Communication/Responses/SimpleResponsePurport.cs new file mode 100644 index 00000000..801e044f --- /dev/null +++ b/SafeExamBrowser.Contracts/Communication/Responses/SimpleResponsePurport.cs @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) + * + * 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; + +namespace SafeExamBrowser.Contracts.Communication.Responses +{ + [Serializable] + public enum SimpleResponsePurport + { + /// + /// Signals an interlocutor that a message has been understood. + /// + Acknowledged = 1, + + /// + /// Signals an interlocutor that a message has not been understood. + /// + UnknownMessage + } +} diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index a3d67b08..f87bbcf4 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -66,13 +66,15 @@ - + + + diff --git a/SafeExamBrowser.Core/Communication/BaseHost.cs b/SafeExamBrowser.Core/Communication/BaseHost.cs index 17fab5f8..338df6e5 100644 --- a/SafeExamBrowser.Core/Communication/BaseHost.cs +++ b/SafeExamBrowser.Core/Communication/BaseHost.cs @@ -8,6 +8,7 @@ using System; using System.ServiceModel; +using System.Threading; using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication.Messages; using SafeExamBrowser.Contracts.Communication.Responses; @@ -18,16 +19,26 @@ namespace SafeExamBrowser.Core.Communication [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)] public abstract class BaseHost : ICommunication, ICommunicationHost { + private const int TWO_SECONDS = 2000; + private readonly object @lock = new object(); + private string address; private ILogger logger; private ServiceHost host; + private Thread hostThread; protected Guid? CommunicationToken { get; private set; } protected ILogger Logger { get; private set; } public bool IsRunning { - get { return host?.State == CommunicationState.Opened; } + get + { + lock (@lock) + { + return host?.State == CommunicationState.Opened; + } + } } public BaseHost(string address, ILogger logger) @@ -39,86 +50,111 @@ namespace SafeExamBrowser.Core.Communication protected abstract bool OnConnect(Guid? token); protected abstract void OnDisconnect(); protected abstract Response OnReceive(Message message); - protected abstract Response OnReceive(MessagePurport message); + protected abstract Response OnReceive(SimpleMessagePurport message); public ConnectionResponse Connect(Guid? token = null) { - logger.Debug($"Received connection request with authentication token '{token}'."); - - var response = new ConnectionResponse(); - var connected = OnConnect(token); - - if (connected) + lock (@lock) { - response.CommunicationToken = CommunicationToken = Guid.NewGuid(); - response.ConnectionEstablished = true; + logger.Debug($"Received connection request with authentication token '{token}'."); + + var response = new ConnectionResponse(); + var connected = OnConnect(token); + + if (connected) + { + response.CommunicationToken = CommunicationToken = Guid.NewGuid(); + response.ConnectionEstablished = true; + } + + logger.Debug($"{(connected ? "Accepted" : "Denied")} connection request."); + + return response; } - - logger.Debug($"{(connected ? "Accepted" : "Denied")} connection request."); - - return response; } public DisconnectionResponse Disconnect(DisconnectionMessage message) { - var response = new DisconnectionResponse(); - - // TODO: Compare with ToString in BaseProxy - needed? - logger.Debug($"Received disconnection request with message '{message}'."); - - if (IsAuthorized(message?.CommunicationToken)) + lock (@lock) { - OnDisconnect(); + var response = new DisconnectionResponse(); - CommunicationToken = null; - response.ConnectionTerminated = true; + logger.Debug($"Received disconnection request with message '{ToString(message)}'."); + + if (IsAuthorized(message?.CommunicationToken)) + { + OnDisconnect(); + + CommunicationToken = null; + response.ConnectionTerminated = true; + } + + return response; } - - return response; } public Response Send(Message message) { - Response response = null; - - logger.Debug($"Received message '{message}'."); - - if (IsAuthorized(message?.CommunicationToken)) + lock (@lock) { - if (message is SimpleMessage) + Response response = null; + + logger.Debug($"Received message '{ToString(message)}'."); + + if (IsAuthorized(message?.CommunicationToken)) { - response = OnReceive((message as SimpleMessage).Purport); - } - else - { - response = OnReceive(message); + if (message is SimpleMessage) + { + response = OnReceive((message as SimpleMessage).Purport); + } + else + { + response = OnReceive(message); + } } + + logger.Debug($"Sending response '{ToString(response)}'."); + + return response; } - - logger.Debug($"Sending response '{response}'."); - - return response; } public void Start() { - host = new ServiceHost(this); - host.AddServiceEndpoint(typeof(ICommunication), new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), address); - host.Closed += Host_Closed; - host.Closing += Host_Closing; - host.Faulted += Host_Faulted; - host.Opened += Host_Opened; - host.Opening += Host_Opening; - host.UnknownMessageReceived += Host_UnknownMessageReceived; - host.Open(); + lock (@lock) + { + var exception = default(Exception); + var startedEvent = new AutoResetEvent(false); - logger.Debug($"Successfully started communication host for endpoint '{address}'."); + hostThread = new Thread(() => TryStartHost(startedEvent, out exception)); + hostThread.SetApartmentState(ApartmentState.STA); + hostThread.IsBackground = true; + hostThread.Start(); + + var success = startedEvent.WaitOne(TWO_SECONDS); + + if (!success) + { + throw new CommunicationException($"Failed to start communication host for endpoint '{address}' within {TWO_SECONDS / 1000} seconds!", exception); + } + } } public void Stop() { - host?.Close(); - logger.Debug($"Terminated communication host for endpoint '{address}'."); + lock (@lock) + { + var success = TryStopHost(out Exception exception); + + if (success) + { + logger.Debug($"Terminated communication host for endpoint '{address}'."); + } + else + { + throw new CommunicationException($"Failed to terminate communication host for endpoint '{address}'!", exception); + } + } } private bool IsAuthorized(Guid? token) @@ -126,6 +162,52 @@ namespace SafeExamBrowser.Core.Communication return CommunicationToken == token; } + private void TryStartHost(AutoResetEvent startedEvent, out Exception exception) + { + exception = null; + + try + { + host = new ServiceHost(this); + host.AddServiceEndpoint(typeof(ICommunication), new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), address); + host.Closed += Host_Closed; + host.Closing += Host_Closing; + host.Faulted += Host_Faulted; + host.Opened += Host_Opened; + host.Opening += Host_Opening; + host.UnknownMessageReceived += Host_UnknownMessageReceived; + host.Open(); + + logger.Debug($"Successfully started communication host for endpoint '{address}'."); + + startedEvent.Set(); + } + catch (Exception e) + { + exception = e; + } + } + + private bool TryStopHost(out Exception exception) + { + var success = false; + + exception = null; + + try + { + host?.Close(); + success = hostThread.Join(TWO_SECONDS); + } + catch (Exception e) + { + exception = e; + success = false; + } + + return success; + } + private void Host_Closed(object sender, EventArgs e) { logger.Debug("Communication host has been closed."); @@ -155,5 +237,15 @@ namespace SafeExamBrowser.Core.Communication { logger.Debug($"Communication host has received an unknown message: {e?.Message}."); } + + private string ToString(Message message) + { + return message != null ? message.ToString() : ""; + } + + private string ToString(Response response) + { + return response != null ? response.ToString() : ""; + } } } diff --git a/SafeExamBrowser.Core/Communication/BaseProxy.cs b/SafeExamBrowser.Core/Communication/BaseProxy.cs index 0d4db168..c46a4977 100644 --- a/SafeExamBrowser.Core/Communication/BaseProxy.cs +++ b/SafeExamBrowser.Core/Communication/BaseProxy.cs @@ -40,7 +40,7 @@ namespace SafeExamBrowser.Core.Communication (channel as ICommunicationObject).Opened += BaseProxy_Opened; (channel as ICommunicationObject).Opening += BaseProxy_Opening; - Logger.Debug($"Trying to connect to endpoint {address} with authentication token '{token}'..."); + Logger.Debug($"Trying to connect to endpoint {address}{(token.HasValue ? $" with authentication token '{token}'" : string.Empty)}..."); var response = channel.Connect(token); @@ -70,21 +70,14 @@ namespace SafeExamBrowser.Core.Communication var response = channel.Send(message); - Logger.Debug($"Sent {ToString(message)}, got {ToString(response)}."); + Logger.Debug($"Sent message '{ToString(message)}', got response '{ToString(response)}'."); return response; } - protected Response Send(MessagePurport purport) + protected Response Send(SimpleMessagePurport purport) { - FailIfNotConnected(nameof(Send)); - - var message = new SimpleMessage { Purport = purport }; - var response = channel.Send(message); - - Logger.Debug($"Sent {ToString(message)}, got {ToString(response)}."); - - return response; + return Send(new SimpleMessage(purport)); } private void BaseProxy_Closed(object sender, EventArgs e) @@ -132,12 +125,12 @@ namespace SafeExamBrowser.Core.Communication private string ToString(Message message) { - return message != null ? $"message of type '{message.GetType()}'" : "no message"; + return message != null ? message.ToString() : ""; } private string ToString(Response response) { - return response != null ? $"response of type '{response.GetType()}'" : "no response"; + return response != null ? response.ToString() : ""; } } } diff --git a/SafeExamBrowser.Core/Communication/ClientProxy.cs b/SafeExamBrowser.Core/Communication/ClientProxy.cs index 26b01a4e..4bfe6d36 100644 --- a/SafeExamBrowser.Core/Communication/ClientProxy.cs +++ b/SafeExamBrowser.Core/Communication/ClientProxy.cs @@ -21,7 +21,9 @@ namespace SafeExamBrowser.Core.Communication public AuthenticationResponse RequestAuthentication() { - return (AuthenticationResponse) Send(MessagePurport.ClientIsReady); + var response = Send(SimpleMessagePurport.Authenticate); + + return response as AuthenticationResponse; } } } diff --git a/SafeExamBrowser.Core/Communication/RuntimeProxy.cs b/SafeExamBrowser.Core/Communication/RuntimeProxy.cs index 3065ff90..f66b7e01 100644 --- a/SafeExamBrowser.Core/Communication/RuntimeProxy.cs +++ b/SafeExamBrowser.Core/Communication/RuntimeProxy.cs @@ -22,12 +22,12 @@ namespace SafeExamBrowser.Core.Communication public ClientConfiguration GetConfiguration() { - return ((ConfigurationResponse) Send(MessagePurport.ConfigurationNeeded)).Configuration; + return ((ConfigurationResponse) Send(SimpleMessagePurport.ConfigurationNeeded)).Configuration; } public void InformClientReady() { - Send(MessagePurport.ClientIsReady); + Send(SimpleMessagePurport.ClientIsReady); } } } diff --git a/SafeExamBrowser.Core/Logging/Logger.cs b/SafeExamBrowser.Core/Logging/Logger.cs index c5c4aaa6..34ab4d5f 100644 --- a/SafeExamBrowser.Core/Logging/Logger.cs +++ b/SafeExamBrowser.Core/Logging/Logger.cs @@ -81,6 +81,15 @@ namespace SafeExamBrowser.Core.Logging details.AppendLine(); details.AppendLine(exception.StackTrace); + for (var inner = exception.InnerException; inner != null; inner = inner.InnerException) + { + details.AppendLine(); + details.AppendLine($" Inner Exception Message: {inner.Message}"); + details.AppendLine($" Inner Exception Type: {inner.GetType()}"); + details.AppendLine(); + details.AppendLine(inner.StackTrace); + } + Add(LogLevel.Error, message); Add(new LogText(details.ToString())); } diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs index 92ae60ca..7d12ebf8 100644 --- a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs +++ b/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs @@ -104,19 +104,19 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations { const int TEN_SECONDS = 10000; - var clientReady = new AutoResetEvent(false); - var clientReadyHandler = new CommunicationEventHandler(() => clientReady.Set()); + var clientStarted = false; + var clientReadyEvent = new AutoResetEvent(false); + var clientReadyEventHandler = new CommunicationEventHandler(() => clientReadyEvent.Set()); var clientExecutable = configuration.RuntimeInfo.ClientExecutablePath; var clientLogFile = $"{'"' + configuration.RuntimeInfo.ClientLogFile + '"'}"; var hostUri = configuration.RuntimeInfo.RuntimeAddress; var token = session.StartupToken.ToString("D"); - runtimeHost.ClientReady += clientReadyHandler; + runtimeHost.ClientReady += clientReadyEventHandler; session.ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, hostUri, token); - var clientStarted = clientReady.WaitOne(TEN_SECONDS); - - runtimeHost.ClientReady -= clientReadyHandler; + clientStarted = clientReadyEvent.WaitOne(TEN_SECONDS); + runtimeHost.ClientReady -= clientReadyEventHandler; // TODO: Check if client process alive! if (clientStarted) @@ -126,7 +126,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations var response = client.RequestAuthentication(); // TODO: Further integrity checks necessary? - if (session.ClientProcess.Id == response.ProcessId) + if (session.ClientProcess.Id == response?.ProcessId) { sessionRunning = true; } diff --git a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs index 60061e66..9b810395 100644 --- a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs +++ b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs @@ -7,7 +7,6 @@ */ using System; -using System.Threading.Tasks; using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication.Messages; using SafeExamBrowser.Contracts.Communication.Responses; @@ -43,21 +42,21 @@ namespace SafeExamBrowser.Runtime.Communication protected override Response OnReceive(Message message) { // TODO - return null; + return new SimpleResponse(SimpleResponsePurport.UnknownMessage); } - protected override Response OnReceive(MessagePurport message) + protected override Response OnReceive(SimpleMessagePurport message) { switch (message) { - case MessagePurport.ClientIsReady: + case SimpleMessagePurport.ClientIsReady: ClientReady?.Invoke(); - break; - case MessagePurport.ConfigurationNeeded: + return new SimpleResponse(SimpleResponsePurport.Acknowledged); + case SimpleMessagePurport.ConfigurationNeeded: return new ConfigurationResponse { Configuration = configuration.BuildClientConfiguration() }; } - return null; + return new SimpleResponse(SimpleResponsePurport.UnknownMessage); } } } diff --git a/SafeExamBrowser.SystemComponents/WirelessNetwork.cs b/SafeExamBrowser.SystemComponents/WirelessNetwork.cs index 0833924b..63dc6b57 100644 --- a/SafeExamBrowser.SystemComponents/WirelessNetwork.cs +++ b/SafeExamBrowser.SystemComponents/WirelessNetwork.cs @@ -23,6 +23,7 @@ namespace SafeExamBrowser.SystemComponents public class WirelessNetwork : ISystemComponent { private const int TWO_SECONDS = 2000; + private readonly object @lock = new object(); private ISystemWirelessNetworkControl control; private ILogger logger; @@ -144,7 +145,7 @@ namespace SafeExamBrowser.SystemComponents private void UpdateControl() { - lock (networks) + lock (@lock) { try { diff --git a/SafeExamBrowser.UserInterface.Classic/RuntimeWindow.xaml b/SafeExamBrowser.UserInterface.Classic/RuntimeWindow.xaml index f2d038ff..bb221a21 100644 --- a/SafeExamBrowser.UserInterface.Classic/RuntimeWindow.xaml +++ b/SafeExamBrowser.UserInterface.Classic/RuntimeWindow.xaml @@ -5,7 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Classic" xmlns:s="clr-namespace:System;assembly=mscorlib" - mc:Ignorable="d" Background="White" Foreground="White" Height="500" Width="750" WindowStyle="None" WindowStartupLocation="CenterScreen" + mc:Ignorable="d" Background="White" Foreground="White" Height="500" Width="850" WindowStyle="None" WindowStartupLocation="CenterScreen" Icon="./Images/SafeExamBrowser.ico" ResizeMode="NoResize" Title="Safe Exam Browser" Topmost="True"> @@ -36,10 +36,10 @@ - + - +