diff --git a/SafeExamBrowser.Communication/Proxies/ServiceProxy.cs b/SafeExamBrowser.Communication/Proxies/ServiceProxy.cs
index 47b3c1d7..95352342 100644
--- a/SafeExamBrowser.Communication/Proxies/ServiceProxy.cs
+++ b/SafeExamBrowser.Communication/Proxies/ServiceProxy.cs
@@ -74,7 +74,7 @@ namespace SafeExamBrowser.Communication.Proxies
{
if (Ignore)
{
- Logger.Debug($"Skipping {operationName} because the ignore flag is set.");
+ Logger.Debug($"Skipping '{operationName}' operation because the ignore flag is set.");
}
return Ignore;
diff --git a/SafeExamBrowser.Configuration/ConfigurationRepository.cs b/SafeExamBrowser.Configuration/ConfigurationRepository.cs
index c6381bcd..b52d4134 100644
--- a/SafeExamBrowser.Configuration/ConfigurationRepository.cs
+++ b/SafeExamBrowser.Configuration/ConfigurationRepository.cs
@@ -25,86 +25,15 @@ namespace SafeExamBrowser.Configuration
private AppConfig appConfig;
- public ISessionData CurrentSession { get; private set; }
- public Settings CurrentSettings { get; private set; }
- public string ReconfigurationFilePath { get; set; }
-
- public AppConfig AppConfig
- {
- get
- {
- if (appConfig == null)
- {
- InitializeAppConfig();
- }
-
- return appConfig;
- }
- }
-
public ConfigurationRepository(string executablePath, string programCopyright, string programTitle, string programVersion)
{
- this.executablePath = executablePath ?? throw new ArgumentNullException(nameof(executablePath));
- this.programCopyright = programCopyright ?? throw new ArgumentNullException(nameof(programCopyright));
- this.programTitle = programTitle ?? throw new ArgumentNullException(nameof(programTitle));
- this.programVersion = programVersion ?? throw new ArgumentNullException(nameof(programVersion));
+ this.executablePath = executablePath ?? string.Empty;
+ this.programCopyright = programCopyright ?? string.Empty;
+ this.programTitle = programTitle ?? string.Empty;
+ this.programVersion = programVersion ?? string.Empty;
}
- public ClientConfiguration BuildClientConfiguration()
- {
- return new ClientConfiguration
- {
- AppConfig = AppConfig,
- SessionId = CurrentSession.Id,
- Settings = CurrentSettings
- };
- }
-
- public void InitializeSessionConfiguration()
- {
- CurrentSession = new SessionData
- {
- Id = Guid.NewGuid(),
- NewDesktop = CurrentSession?.NewDesktop,
- OriginalDesktop = CurrentSession?.OriginalDesktop,
- StartupToken = Guid.NewGuid()
- };
-
- UpdateAppConfig();
- }
-
- public LoadStatus LoadSettings(Uri resource, string settingsPassword = null, string adminPassword = null)
- {
- // TODO: Implement loading mechanism
-
- LoadDefaultSettings();
-
- return LoadStatus.Success;
- }
-
- public void LoadDefaultSettings()
- {
- // TODO: Implement default settings
-
- CurrentSettings = new Settings();
-
- CurrentSettings.KioskMode = KioskMode.None;
- CurrentSettings.ServicePolicy = ServicePolicy.Optional;
-
- CurrentSettings.Browser.StartUrl = "https://www.safeexambrowser.org/testing";
- CurrentSettings.Browser.AllowAddressBar = true;
- CurrentSettings.Browser.AllowBackwardNavigation = true;
- CurrentSettings.Browser.AllowDeveloperConsole = true;
- CurrentSettings.Browser.AllowForwardNavigation = true;
- CurrentSettings.Browser.AllowReloading = true;
- CurrentSettings.Browser.AllowDownloads = true;
-
- CurrentSettings.Taskbar.AllowApplicationLog = true;
- CurrentSettings.Taskbar.AllowKeyboardLayout = true;
- CurrentSettings.Taskbar.AllowWirelessNetwork = true;
- }
-
- private void InitializeAppConfig()
+ public AppConfig InitializeAppConfig()
{
var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(SafeExamBrowser));
var startTime = DateTime.Now;
@@ -134,12 +63,89 @@ namespace SafeExamBrowser.Configuration
appConfig.SebUriScheme = "seb";
appConfig.SebUriSchemeSecure = "sebs";
appConfig.ServiceAddress = $"{BASE_ADDRESS}/service";
+
+ return appConfig;
+ }
+
+ public ISessionConfiguration InitializeSessionConfiguration()
+ {
+ var configuration = new SessionConfiguration();
+
+ UpdateAppConfig();
+
+ configuration.AppConfig = CloneAppConfig();
+ configuration.Id = Guid.NewGuid();
+ configuration.StartupToken = Guid.NewGuid();
+
+ return configuration;
+ }
+
+ public LoadStatus TryLoadSettings(Uri resource, out Settings settings, string adminPassword = null, string settingsPassword = null)
+ {
+ // TODO: Implement loading mechanism
+
+ settings = LoadDefaultSettings();
+
+ return LoadStatus.Success;
+ }
+
+ public Settings LoadDefaultSettings()
+ {
+ // TODO: Implement default settings
+
+ var settings = new Settings();
+
+ settings.KioskMode = new Random().Next(10) < 5 ? KioskMode.CreateNewDesktop : KioskMode.DisableExplorerShell;
+ settings.ServicePolicy = ServicePolicy.Optional;
+
+ settings.Browser.StartUrl = "https://www.safeexambrowser.org/testing";
+ settings.Browser.AllowAddressBar = true;
+ settings.Browser.AllowBackwardNavigation = true;
+ settings.Browser.AllowDeveloperConsole = true;
+ settings.Browser.AllowForwardNavigation = true;
+ settings.Browser.AllowReloading = true;
+ settings.Browser.AllowDownloads = true;
+
+ settings.Taskbar.AllowApplicationLog = true;
+ settings.Taskbar.AllowKeyboardLayout = true;
+ settings.Taskbar.AllowWirelessNetwork = true;
+
+ return settings;
+ }
+
+ private AppConfig CloneAppConfig()
+ {
+ return new AppConfig
+ {
+ AppDataFolder = appConfig.AppDataFolder,
+ ApplicationStartTime = appConfig.ApplicationStartTime,
+ BrowserCachePath = appConfig.BrowserCachePath,
+ BrowserLogFile = appConfig.BrowserLogFile,
+ ClientAddress = appConfig.ClientAddress,
+ ClientExecutablePath = appConfig.ClientExecutablePath,
+ ClientId = appConfig.ClientId,
+ ClientLogFile = appConfig.ClientLogFile,
+ ConfigurationFileExtension = appConfig.ConfigurationFileExtension,
+ DefaultSettingsFileName = appConfig.DefaultSettingsFileName,
+ DownloadDirectory = appConfig.DownloadDirectory,
+ LogLevel = appConfig.LogLevel,
+ ProgramCopyright = appConfig.ProgramCopyright,
+ ProgramDataFolder = appConfig.ProgramDataFolder,
+ ProgramTitle = appConfig.ProgramTitle,
+ ProgramVersion = appConfig.ProgramVersion,
+ RuntimeAddress = appConfig.RuntimeAddress,
+ RuntimeId = appConfig.RuntimeId,
+ RuntimeLogFile = appConfig.RuntimeLogFile,
+ SebUriScheme = appConfig.SebUriScheme,
+ SebUriSchemeSecure = appConfig.SebUriSchemeSecure,
+ ServiceAddress = appConfig.ServiceAddress
+ };
}
private void UpdateAppConfig()
{
- AppConfig.ClientId = Guid.NewGuid();
- AppConfig.ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}";
+ appConfig.ClientId = Guid.NewGuid();
+ appConfig.ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}";
}
}
}
diff --git a/SafeExamBrowser.Configuration/ResourceLoader.cs b/SafeExamBrowser.Configuration/ResourceLoader.cs
index f6b353d7..6bca4974 100644
--- a/SafeExamBrowser.Configuration/ResourceLoader.cs
+++ b/SafeExamBrowser.Configuration/ResourceLoader.cs
@@ -15,7 +15,7 @@ namespace SafeExamBrowser.Configuration
{
public bool IsHtmlResource(Uri resource)
{
- // TODO
+ // TODO: Implement resource loader
return false;
}
}
diff --git a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj
index 53eef41a..0947634c 100644
--- a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj
+++ b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj
@@ -53,9 +53,9 @@
-
+
diff --git a/SafeExamBrowser.Configuration/SessionData.cs b/SafeExamBrowser.Configuration/SessionConfiguration.cs
similarity index 59%
rename from SafeExamBrowser.Configuration/SessionData.cs
rename to SafeExamBrowser.Configuration/SessionConfiguration.cs
index c256d342..24a93cef 100644
--- a/SafeExamBrowser.Configuration/SessionData.cs
+++ b/SafeExamBrowser.Configuration/SessionConfiguration.cs
@@ -7,19 +7,16 @@
*/
using System;
-using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Configuration;
-using SafeExamBrowser.Contracts.WindowsApi;
+using SafeExamBrowser.Contracts.Configuration.Settings;
namespace SafeExamBrowser.Configuration
{
- public class SessionData : ISessionData
+ internal class SessionConfiguration : ISessionConfiguration
{
- public IClientProxy ClientProxy { get; set; }
- public IProcess ClientProcess { get; set; }
+ public AppConfig AppConfig { get; set; }
public Guid Id { get; set; }
- public IDesktop NewDesktop { get; set; }
- public IDesktop OriginalDesktop { get; set; }
+ public Settings Settings { get; set; }
public Guid StartupToken { get; set; }
}
}
diff --git a/SafeExamBrowser.Contracts/Communication/Events/ClientConfigurationEventArgs.cs b/SafeExamBrowser.Contracts/Communication/Events/ClientConfigurationEventArgs.cs
new file mode 100644
index 00000000..045ceb59
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Events/ClientConfigurationEventArgs.cs
@@ -0,0 +1,23 @@
+/*
+ * 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 SafeExamBrowser.Contracts.Configuration;
+
+namespace SafeExamBrowser.Contracts.Communication.Events
+{
+ ///
+ /// The event arguments used for the client configuration event fired by the .
+ ///
+ public class ClientConfigurationEventArgs : CommunicationEventArgs
+ {
+ ///
+ /// The configuration to be sent to the client.
+ ///
+ public ClientConfiguration ClientConfiguration { get; set; }
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Hosts/IRuntimeHost.cs b/SafeExamBrowser.Contracts/Communication/Hosts/IRuntimeHost.cs
index e9e8011d..63d99315 100644
--- a/SafeExamBrowser.Contracts/Communication/Hosts/IRuntimeHost.cs
+++ b/SafeExamBrowser.Contracts/Communication/Hosts/IRuntimeHost.cs
@@ -31,6 +31,11 @@ namespace SafeExamBrowser.Contracts.Communication.Hosts
///
event CommunicationEventHandler ClientReady;
+ ///
+ /// Event fired when the client requested its configuration data.
+ ///
+ event CommunicationEventHandler ClientConfigurationNeeded;
+
///
/// Event fired when the client submitted a password entered by the user.
///
diff --git a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs
index 670ac24e..319f7a8b 100644
--- a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs
+++ b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs
@@ -16,46 +16,25 @@ namespace SafeExamBrowser.Contracts.Configuration
public interface IConfigurationRepository
{
///
- /// The global configuration information for the currently running application instance.
+ /// Initializes the global configuration information for the currently running application instance.
///
- AppConfig AppConfig { get; }
+ AppConfig InitializeAppConfig();
///
- /// Retrieves the current session data, i.e. the last one which was initialized. If no session has been initialized yet, this
- /// property will be null!
+ /// Initializes all relevant configuration data for a new session.
///
- ISessionData CurrentSession { get; }
-
- ///
- /// Retrieves the current settings, i.e. the last ones which were loaded. If no settings have been loaded yet, this property will
- /// be null!
- ///
- Settings.Settings CurrentSettings { get; }
-
- ///
- /// The path of the settings file to be used when reconfiguring the application.
- ///
- string ReconfigurationFilePath { get; set; }
-
- ///
- /// Builds a configuration for the client component, given the currently loaded settings, session and runtime information.
- ///
- ClientConfiguration BuildClientConfiguration();
-
- ///
- /// Initializes all relevant data for a new session.
- ///
- void InitializeSessionConfiguration();
+ ISessionConfiguration InitializeSessionConfiguration();
///
/// Attempts to load settings from the specified resource, using the optional passwords. Returns a
- /// indicating the result of the operation.
+ /// indicating the result of the operation. As long as the result is not , the declared
+ /// will be null!
///
- LoadStatus LoadSettings(Uri resource, string adminPassword = null, string settingsPassword = null);
+ LoadStatus TryLoadSettings(Uri resource, out Settings.Settings settings, string adminPassword = null, string settingsPassword = null);
///
/// Loads the default settings.
///
- void LoadDefaultSettings();
+ Settings.Settings LoadDefaultSettings();
}
}
diff --git a/SafeExamBrowser.Contracts/Configuration/ISessionConfiguration.cs b/SafeExamBrowser.Contracts/Configuration/ISessionConfiguration.cs
new file mode 100644
index 00000000..65c570f5
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Configuration/ISessionConfiguration.cs
@@ -0,0 +1,38 @@
+/*
+ * 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.Configuration
+{
+ ///
+ /// Holds all session-related configuration data.
+ ///
+ public interface ISessionConfiguration
+ {
+ ///
+ /// The active application configuration for this session.
+ ///
+ AppConfig AppConfig { get; }
+
+ ///
+ /// The unique session identifier.
+ ///
+ Guid Id { get; }
+
+ ///
+ /// The settings used for this session.
+ ///
+ Settings.Settings Settings { get; set; }
+
+ ///
+ /// The startup token used by the client and runtime components for initial authentication.
+ ///
+ Guid StartupToken { get; }
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Configuration/ISessionData.cs b/SafeExamBrowser.Contracts/Configuration/ISessionData.cs
deleted file mode 100644
index 5b7a27b0..00000000
--- a/SafeExamBrowser.Contracts/Configuration/ISessionData.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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;
-using SafeExamBrowser.Contracts.Communication.Proxies;
-using SafeExamBrowser.Contracts.WindowsApi;
-
-namespace SafeExamBrowser.Contracts.Configuration
-{
- ///
- /// Holds all session-related configuration and runtime data.
- ///
- public interface ISessionData
- {
- ///
- /// The communication proxy for the client instance associated to this session.
- ///
- IClientProxy ClientProxy { get; set; }
-
- ///
- /// The process information of the client instance associated to this session.
- ///
- IProcess ClientProcess { get; set; }
-
- ///
- /// The unique session identifier.
- ///
- Guid Id { get; }
-
- ///
- /// The new desktop, if is active for this session.
- ///
- IDesktop NewDesktop { get; set; }
-
- ///
- /// The original desktop, if is active for this session.
- ///
- IDesktop OriginalDesktop { get; set; }
-
- ///
- /// The startup token used by the client and runtime components for initial authentication.
- ///
- Guid StartupToken { get; }
- }
-}
diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
index 28401664..9224aafd 100644
--- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
+++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
@@ -52,6 +52,7 @@
+
@@ -109,7 +110,7 @@
-
+
diff --git a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
index b6a934c2..b937ca6c 100644
--- a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
+++ b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
@@ -11,7 +11,6 @@ using SafeExamBrowser.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.Communication.Events;
using SafeExamBrowser.Contracts.Communication.Hosts;
-using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Runtime.Communication
@@ -19,24 +18,18 @@ namespace SafeExamBrowser.Runtime.Communication
internal class RuntimeHost : BaseHost, IRuntimeHost
{
private bool allowConnection = true;
- private IConfigurationRepository configuration;
public Guid StartupToken { private get; set; }
public event CommunicationEventHandler ClientDisconnected;
public event CommunicationEventHandler ClientReady;
+ public event CommunicationEventHandler ClientConfigurationNeeded;
public event CommunicationEventHandler PasswordReceived;
public event CommunicationEventHandler ReconfigurationRequested;
public event CommunicationEventHandler ShutdownRequested;
- public RuntimeHost(
- string address,
- IConfigurationRepository configuration,
- IHostObjectFactory factory,
- ILogger logger,
- int timeout_ms) : base(address, factory, logger, timeout_ms)
+ public RuntimeHost(string address, IHostObjectFactory factory, ILogger logger, int timeout_ms) : base(address, factory, logger, timeout_ms)
{
- this.configuration = configuration;
}
protected override bool OnConnect(Guid? token = null)
@@ -87,8 +80,7 @@ namespace SafeExamBrowser.Runtime.Communication
ClientReady?.Invoke();
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
case SimpleMessagePurport.ConfigurationNeeded:
- // TODO: Not the job of the host, fire event or alike!
- return new ConfigurationResponse { Configuration = configuration.BuildClientConfiguration() };
+ return HandleConfigurationRequest();
case SimpleMessagePurport.RequestShutdown:
ShutdownRequested?.Invoke();
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
@@ -96,5 +88,14 @@ namespace SafeExamBrowser.Runtime.Communication
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
}
+
+ private Response HandleConfigurationRequest()
+ {
+ var args = new ClientConfigurationEventArgs();
+
+ ClientConfigurationNeeded?.Invoke(args);
+
+ return new ConfigurationResponse { Configuration = args.ClientConfiguration };
+ }
}
}
diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs
index 6588e256..6f52867c 100644
--- a/SafeExamBrowser.Runtime/CompositionRoot.cs
+++ b/SafeExamBrowser.Runtime/CompositionRoot.cs
@@ -49,7 +49,7 @@ namespace SafeExamBrowser.Runtime
var nativeMethods = new NativeMethods();
logger = new Logger();
- appConfig = configuration.AppConfig;
+ appConfig = configuration.InitializeAppConfig();
systemInfo = new SystemInfo();
InitializeLogging();
@@ -61,8 +61,9 @@ namespace SafeExamBrowser.Runtime
var processFactory = new ProcessFactory(new ModuleLogger(logger, nameof(ProcessFactory)));
var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), logger);
var resourceLoader = new ResourceLoader();
- var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, configuration, new HostObjectFactory(), new ModuleLogger(logger, nameof(RuntimeHost)), FIVE_SECONDS);
+ var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, new HostObjectFactory(), new ModuleLogger(logger, nameof(RuntimeHost)), FIVE_SECONDS);
var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(ServiceProxy)));
+ var sessionContext = new SessionContext();
var uiFactory = new UserInterfaceFactory(text);
var bootstrapOperations = new Queue();
@@ -71,18 +72,19 @@ namespace SafeExamBrowser.Runtime
bootstrapOperations.Enqueue(new I18nOperation(logger, text, textResource));
bootstrapOperations.Enqueue(new CommunicationHostOperation(runtimeHost, logger));
- sessionOperations.Enqueue(new ConfigurationOperation(appConfig, configuration, logger, resourceLoader, args));
- sessionOperations.Enqueue(new ClientTerminationOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, FIFTEEN_SECONDS));
- sessionOperations.Enqueue(new KioskModeTerminationOperation(configuration, desktopFactory, explorerShell, logger, processFactory));
- sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost));
- sessionOperations.Enqueue(new ServiceOperation(configuration, logger, serviceProxy));
- sessionOperations.Enqueue(new KioskModeOperation(configuration, desktopFactory, explorerShell, logger, processFactory));
- sessionOperations.Enqueue(new ClientOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, FIFTEEN_SECONDS));
+ sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost, sessionContext));
+ sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, logger, resourceLoader, sessionContext));
+ sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, FIFTEEN_SECONDS));
+ sessionOperations.Enqueue(new KioskModeTerminationOperation(desktopFactory, explorerShell, logger, processFactory, sessionContext));
+ sessionOperations.Enqueue(new ServiceOperation(logger, serviceProxy, sessionContext));
+ sessionOperations.Enqueue(new KioskModeOperation(desktopFactory, explorerShell, logger, processFactory, sessionContext));
+ sessionOperations.Enqueue(new ClientOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, FIFTEEN_SECONDS));
+ sessionOperations.Enqueue(new SessionActivationOperation(logger, sessionContext));
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
var sessionSequence = new RepeatableOperationSequence(logger, sessionOperations);
- RuntimeController = new RuntimeController(appConfig, configuration, logger, messageBox, bootstrapSequence, sessionSequence, runtimeHost, serviceProxy, shutdown, text, uiFactory);
+ RuntimeController = new RuntimeController(appConfig, logger, messageBox, bootstrapSequence, sessionSequence, runtimeHost, serviceProxy, sessionContext, shutdown, text, uiFactory);
}
internal void LogStartupInformation()
@@ -103,7 +105,7 @@ namespace SafeExamBrowser.Runtime
logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
- private ConfigurationRepository BuildConfigurationRepository()
+ private IConfigurationRepository BuildConfigurationRepository()
{
var executable = Assembly.GetExecutingAssembly();
var programCopyright = executable.GetCustomAttribute().Copyright;
diff --git a/SafeExamBrowser.Runtime/Operations/ClientOperation.cs b/SafeExamBrowser.Runtime/Operations/ClientOperation.cs
index 83632123..7def87c3 100644
--- a/SafeExamBrowser.Runtime/Operations/ClientOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/ClientOperation.cs
@@ -10,7 +10,6 @@ using System.Threading;
using SafeExamBrowser.Contracts.Communication.Events;
using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Proxies;
-using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
using SafeExamBrowser.Contracts.I18n;
@@ -20,40 +19,38 @@ using SafeExamBrowser.Contracts.WindowsApi.Events;
namespace SafeExamBrowser.Runtime.Operations
{
- internal class ClientOperation : IRepeatableOperation
+ internal class ClientOperation : SessionOperation
{
private readonly int timeout_ms;
- private IConfigurationRepository configuration;
private ILogger logger;
private IProcessFactory processFactory;
private IProxyFactory proxyFactory;
private IRuntimeHost runtimeHost;
- public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
- public event StatusChangedEventHandler StatusChanged;
-
private IProcess ClientProcess
{
- get { return configuration.CurrentSession.ClientProcess; }
- set { configuration.CurrentSession.ClientProcess = value; }
+ get { return Context.ClientProcess; }
+ set { Context.ClientProcess = value; }
}
private IClientProxy ClientProxy
{
- get { return configuration.CurrentSession.ClientProxy; }
- set { configuration.CurrentSession.ClientProxy = value; }
+ get { return Context.ClientProxy; }
+ set { Context.ClientProxy = value; }
}
+ public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
+ public override event StatusChangedEventHandler StatusChanged;
+
public ClientOperation(
- IConfigurationRepository configuration,
ILogger logger,
IProcessFactory processFactory,
IProxyFactory proxyFactory,
IRuntimeHost runtimeHost,
- int timeout_ms)
+ SessionContext sessionContext,
+ int timeout_ms) : base(sessionContext)
{
- this.configuration = configuration;
this.logger = logger;
this.processFactory = processFactory;
this.proxyFactory = proxyFactory;
@@ -61,7 +58,7 @@ namespace SafeExamBrowser.Runtime.Operations
this.timeout_ms = timeout_ms;
}
- public virtual OperationResult Perform()
+ public override OperationResult Perform()
{
StatusChanged?.Invoke(TextKey.OperationStatus_StartClient);
@@ -79,12 +76,12 @@ namespace SafeExamBrowser.Runtime.Operations
return success ? OperationResult.Success : OperationResult.Failed;
}
- public virtual OperationResult Repeat()
+ public override OperationResult Repeat()
{
return Perform();
}
- public virtual OperationResult Revert()
+ public override OperationResult Revert()
{
var success = true;
@@ -103,10 +100,10 @@ namespace SafeExamBrowser.Runtime.Operations
var clientReadyEvent = new AutoResetEvent(false);
var clientReadyEventHandler = new CommunicationEventHandler(() => clientReadyEvent.Set());
- var clientExecutable = configuration.AppConfig.ClientExecutablePath;
- var clientLogFile = $"{'"' + configuration.AppConfig.ClientLogFile + '"'}";
- var hostUri = configuration.AppConfig.RuntimeAddress;
- var token = configuration.CurrentSession.StartupToken.ToString("D");
+ var clientExecutable = Context.Next.AppConfig.ClientExecutablePath;
+ var clientLogFile = $"{'"' + Context.Next.AppConfig.ClientLogFile + '"'}";
+ var hostUri = Context.Next.AppConfig.RuntimeAddress;
+ var token = Context.Next.StartupToken.ToString("D");
logger.Info("Starting new client process...");
runtimeHost.ClientReady += clientReadyEventHandler;
@@ -124,9 +121,9 @@ namespace SafeExamBrowser.Runtime.Operations
}
logger.Info("Client has been successfully started and initialized. Creating communication proxy for client host...");
- ClientProxy = proxyFactory.CreateClientProxy(configuration.AppConfig.ClientAddress);
+ ClientProxy = proxyFactory.CreateClientProxy(Context.Next.AppConfig.ClientAddress);
- if (!ClientProxy.Connect(configuration.CurrentSession.StartupToken))
+ if (!ClientProxy.Connect(Context.Next.StartupToken))
{
logger.Error("Failed to connect to client!");
diff --git a/SafeExamBrowser.Runtime/Operations/ClientTerminationOperation.cs b/SafeExamBrowser.Runtime/Operations/ClientTerminationOperation.cs
index af3d9e0d..20fcd710 100644
--- a/SafeExamBrowser.Runtime/Operations/ClientTerminationOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/ClientTerminationOperation.cs
@@ -8,22 +8,21 @@
using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Proxies;
-using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.WindowsApi;
namespace SafeExamBrowser.Runtime.Operations
{
- internal class ClientTerminationOperation : ClientOperation, IRepeatableOperation
+ internal class ClientTerminationOperation : ClientOperation
{
public ClientTerminationOperation(
- IConfigurationRepository configuration,
ILogger logger,
IProcessFactory processFactory,
IProxyFactory proxyFactory,
IRuntimeHost runtimeHost,
- int timeout_ms) : base(configuration, logger, processFactory, proxyFactory, runtimeHost, timeout_ms)
+ SessionContext sessionContext,
+ int timeout_ms) : base(logger, processFactory, proxyFactory, runtimeHost, sessionContext, timeout_ms)
{
}
diff --git a/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs
index b0f6e9fa..3270588e 100644
--- a/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs
@@ -19,32 +19,30 @@ using SafeExamBrowser.Runtime.Operations.Events;
namespace SafeExamBrowser.Runtime.Operations
{
- internal class ConfigurationOperation : IRepeatableOperation
+ internal class ConfigurationOperation : SessionOperation
{
+ private string[] commandLineArgs;
private IConfigurationRepository configuration;
private ILogger logger;
private IResourceLoader resourceLoader;
- private AppConfig appConfig;
- private string[] commandLineArgs;
- public event ActionRequiredEventHandler ActionRequired;
- public event StatusChangedEventHandler StatusChanged;
+ public override event ActionRequiredEventHandler ActionRequired;
+ public override event StatusChangedEventHandler StatusChanged;
public ConfigurationOperation(
- AppConfig appConfig,
+ string[] commandLineArgs,
IConfigurationRepository configuration,
ILogger logger,
IResourceLoader resourceLoader,
- string[] commandLineArgs)
+ SessionContext sessionContext) : base(sessionContext)
{
- this.appConfig = appConfig;
+ this.commandLineArgs = commandLineArgs;
this.logger = logger;
this.configuration = configuration;
this.resourceLoader = resourceLoader;
- this.commandLineArgs = commandLineArgs;
}
- public OperationResult Perform()
+ public override OperationResult Perform()
{
logger.Info("Initializing application configuration...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeConfiguration);
@@ -69,12 +67,12 @@ namespace SafeExamBrowser.Runtime.Operations
return OperationResult.Success;
}
- public OperationResult Repeat()
+ public override OperationResult Repeat()
{
logger.Info("Initializing new application configuration...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeConfiguration);
- var isValidUri = TryValidateSettingsUri(configuration.ReconfigurationFilePath, out Uri uri);
+ var isValidUri = TryValidateSettingsUri(Context.ReconfigurationFilePath, out Uri uri);
if (isValidUri)
{
@@ -92,7 +90,7 @@ namespace SafeExamBrowser.Runtime.Operations
return OperationResult.Failed;
}
- public OperationResult Revert()
+ public override OperationResult Revert()
{
return OperationResult.Success;
}
@@ -101,11 +99,12 @@ namespace SafeExamBrowser.Runtime.Operations
{
var adminPassword = default(string);
var settingsPassword = default(string);
+ var settings = default(Settings);
var status = default(LoadStatus);
for (int adminAttempts = 0, settingsAttempts = 0; adminAttempts < 5 && settingsAttempts < 5;)
{
- status = configuration.LoadSettings(uri, adminPassword, settingsPassword);
+ status = configuration.TryLoadSettings(uri, out settings, adminPassword, settingsPassword);
if (status == LoadStatus.AdminPasswordNeeded || status == LoadStatus.SettingsPasswordNeeded)
{
@@ -132,7 +131,15 @@ namespace SafeExamBrowser.Runtime.Operations
HandleInvalidData(ref status, uri);
}
- return status == LoadStatus.Success ? OperationResult.Success : OperationResult.Failed;
+ if (status == LoadStatus.Success)
+ {
+ Context.Next.Settings = settings;
+
+ return OperationResult.Success;
+ }
+
+
+ return OperationResult.Failed;
}
private PasswordRequiredEventArgs TryGetPassword(LoadStatus status)
@@ -150,7 +157,7 @@ namespace SafeExamBrowser.Runtime.Operations
if (resourceLoader.IsHtmlResource(uri))
{
configuration.LoadDefaultSettings();
- configuration.CurrentSettings.Browser.StartUrl = uri.AbsoluteUri;
+ Context.Next.Settings.Browser.StartUrl = uri.AbsoluteUri;
logger.Info($"The specified URI '{uri.AbsoluteUri}' appears to point to a HTML resource, setting it as startup URL.");
status = LoadStatus.Success;
@@ -165,8 +172,8 @@ namespace SafeExamBrowser.Runtime.Operations
{
var path = string.Empty;
var isValidUri = false;
- var programDataSettings = Path.Combine(appConfig.ProgramDataFolder, appConfig.DefaultSettingsFileName);
- var appDataSettings = Path.Combine(appConfig.AppDataFolder, appConfig.DefaultSettingsFileName);
+ var programDataSettings = Path.Combine(Context.Next.AppConfig.ProgramDataFolder, Context.Next.AppConfig.DefaultSettingsFileName);
+ var appDataSettings = Path.Combine(Context.Next.AppConfig.AppDataFolder, Context.Next.AppConfig.DefaultSettingsFileName);
uri = null;
@@ -206,7 +213,7 @@ namespace SafeExamBrowser.Runtime.Operations
private void HandleClientConfiguration(ref OperationResult result)
{
- if (result == OperationResult.Success && configuration.CurrentSettings.ConfigurationMode == ConfigurationMode.ConfigureClient)
+ if (result == OperationResult.Success && Context.Next.Settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
{
var args = new ConfigurationCompletedEventArgs();
diff --git a/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs b/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs
index 0f6a3361..024c017d 100644
--- a/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs
@@ -6,7 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
@@ -16,52 +15,40 @@ using SafeExamBrowser.Contracts.WindowsApi;
namespace SafeExamBrowser.Runtime.Operations
{
- internal class KioskModeOperation : IRepeatableOperation
+ internal class KioskModeOperation : SessionOperation
{
- private IConfigurationRepository configuration;
- private IDesktopFactory desktopFactory;
- private IExplorerShell explorerShell;
- private KioskMode kioskMode;
- private ILogger logger;
- private IProcessFactory processFactory;
+ protected IDesktopFactory desktopFactory;
+ protected IExplorerShell explorerShell;
+ protected ILogger logger;
+ protected IProcessFactory processFactory;
- public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
- public event StatusChangedEventHandler StatusChanged;
+ private static IDesktop newDesktop;
+ private static IDesktop originalDesktop;
- private IDesktop NewDesktop
- {
- get { return configuration.CurrentSession.NewDesktop; }
- set { configuration.CurrentSession.NewDesktop = value; }
- }
+ protected static KioskMode? ActiveMode { get; private set; }
- private IDesktop OriginalDesktop
- {
- get { return configuration.CurrentSession.OriginalDesktop; }
- set { configuration.CurrentSession.OriginalDesktop = value; }
- }
+ public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
+ public override event StatusChangedEventHandler StatusChanged;
public KioskModeOperation(
- IConfigurationRepository configuration,
IDesktopFactory desktopFactory,
IExplorerShell explorerShell,
ILogger logger,
- IProcessFactory processFactory)
+ IProcessFactory processFactory,
+ SessionContext sessionContext) : base(sessionContext)
{
- this.configuration = configuration;
this.desktopFactory = desktopFactory;
this.explorerShell = explorerShell;
this.logger = logger;
this.processFactory = processFactory;
}
- public virtual OperationResult Perform()
+ public override OperationResult Perform()
{
- kioskMode = configuration.CurrentSettings.KioskMode;
-
- logger.Info($"Initializing kiosk mode '{kioskMode}'...");
+ logger.Info($"Initializing kiosk mode '{Context.Next.Settings.KioskMode}'...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeKioskMode);
- switch (kioskMode)
+ switch (Context.Next.Settings.KioskMode)
{
case KioskMode.CreateNewDesktop:
CreateNewDesktop();
@@ -71,17 +58,18 @@ namespace SafeExamBrowser.Runtime.Operations
break;
}
+ ActiveMode = Context.Next.Settings.KioskMode;
+
return OperationResult.Success;
}
- public virtual OperationResult Repeat()
+ public override OperationResult Repeat()
{
- var oldMode = kioskMode;
- var newMode = configuration.CurrentSettings.KioskMode;
+ var newMode = Context.Next.Settings.KioskMode;
- if (newMode == oldMode)
+ if (ActiveMode == newMode)
{
- logger.Info($"New kiosk mode '{newMode}' is equal to the currently active '{oldMode}', skipping re-initialization...");
+ logger.Info($"New kiosk mode '{newMode}' is already active, skipping initialization...");
return OperationResult.Success;
}
@@ -89,12 +77,12 @@ namespace SafeExamBrowser.Runtime.Operations
return Perform();
}
- public virtual OperationResult Revert()
+ public override OperationResult Revert()
{
- logger.Info($"Reverting kiosk mode '{kioskMode}'...");
+ logger.Info($"Reverting kiosk mode '{ActiveMode}'...");
StatusChanged?.Invoke(TextKey.OperationStatus_RevertKioskMode);
- switch (kioskMode)
+ switch (ActiveMode)
{
case KioskMode.CreateNewDesktop:
CloseNewDesktop();
@@ -109,14 +97,14 @@ namespace SafeExamBrowser.Runtime.Operations
private void CreateNewDesktop()
{
- OriginalDesktop = desktopFactory.GetCurrent();
- logger.Info($"Current desktop is {OriginalDesktop}.");
+ originalDesktop = desktopFactory.GetCurrent();
+ logger.Info($"Current desktop is {originalDesktop}.");
- NewDesktop = desktopFactory.CreateNew(nameof(SafeExamBrowser));
- logger.Info($"Created new desktop {NewDesktop}.");
+ newDesktop = desktopFactory.CreateNew(nameof(SafeExamBrowser));
+ logger.Info($"Created new desktop {newDesktop}.");
- NewDesktop.Activate();
- processFactory.StartupDesktop = NewDesktop;
+ newDesktop.Activate();
+ processFactory.StartupDesktop = newDesktop;
logger.Info("Successfully activated new desktop.");
explorerShell.Suspend();
@@ -124,21 +112,21 @@ namespace SafeExamBrowser.Runtime.Operations
private void CloseNewDesktop()
{
- if (OriginalDesktop != null)
+ if (originalDesktop != null)
{
- OriginalDesktop.Activate();
- processFactory.StartupDesktop = OriginalDesktop;
- logger.Info($"Switched back to original desktop {OriginalDesktop}.");
+ originalDesktop.Activate();
+ processFactory.StartupDesktop = originalDesktop;
+ logger.Info($"Switched back to original desktop {originalDesktop}.");
}
else
{
logger.Warn($"No original desktop found when attempting to close new desktop!");
}
- if (NewDesktop != null)
+ if (newDesktop != null)
{
- NewDesktop.Close();
- logger.Info($"Closed new desktop {NewDesktop}.");
+ newDesktop.Close();
+ logger.Info($"Closed new desktop {newDesktop}.");
}
else
{
@@ -151,6 +139,9 @@ namespace SafeExamBrowser.Runtime.Operations
private void TerminateExplorerShell()
{
StatusChanged?.Invoke(TextKey.OperationStatus_WaitExplorerTermination);
+
+ // TODO: Hiding all windows must be done here, as the explorer shell is needed to do so!
+
explorerShell.Terminate();
}
@@ -158,6 +149,8 @@ namespace SafeExamBrowser.Runtime.Operations
{
StatusChanged?.Invoke(TextKey.OperationStatus_WaitExplorerStartup);
explorerShell.Start();
+
+ // TODO: Restore all hidden windows!
}
}
}
diff --git a/SafeExamBrowser.Runtime/Operations/KioskModeTerminationOperation.cs b/SafeExamBrowser.Runtime/Operations/KioskModeTerminationOperation.cs
index 09f26bae..04555167 100644
--- a/SafeExamBrowser.Runtime/Operations/KioskModeTerminationOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/KioskModeTerminationOperation.cs
@@ -6,8 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-using SafeExamBrowser.Contracts.Configuration;
-using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.WindowsApi;
@@ -16,36 +14,27 @@ namespace SafeExamBrowser.Runtime.Operations
{
internal class KioskModeTerminationOperation : KioskModeOperation, IRepeatableOperation
{
- private IConfigurationRepository configuration;
- private KioskMode kioskMode;
- private ILogger logger;
-
public KioskModeTerminationOperation(
- IConfigurationRepository configuration,
IDesktopFactory desktopFactory,
IExplorerShell explorerShell,
ILogger logger,
- IProcessFactory processFactory) : base(configuration, desktopFactory, explorerShell, logger, processFactory)
+ IProcessFactory processFactory,
+ SessionContext sessionContext) : base(desktopFactory, explorerShell, logger, processFactory, sessionContext)
{
- this.configuration = configuration;
- this.logger = logger;
}
public override OperationResult Perform()
{
- kioskMode = configuration.CurrentSettings.KioskMode;
-
return OperationResult.Success;
}
public override OperationResult Repeat()
{
- var oldMode = kioskMode;
- var newMode = configuration.CurrentSettings.KioskMode;
+ var newMode = Context.Next.Settings.KioskMode;
- if (newMode == oldMode)
+ if (ActiveMode == newMode)
{
- logger.Info($"New kiosk mode '{newMode}' is equal to the currently active '{oldMode}', skipping termination...");
+ logger.Info($"New kiosk mode '{newMode}' is the same as the currently active, skipping termination...");
return OperationResult.Success;
}
diff --git a/SafeExamBrowser.Runtime/Operations/ServiceOperation.cs b/SafeExamBrowser.Runtime/Operations/ServiceOperation.cs
index 92aac37a..d5a62503 100644
--- a/SafeExamBrowser.Runtime/Operations/ServiceOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/ServiceOperation.cs
@@ -7,7 +7,6 @@
*/
using SafeExamBrowser.Contracts.Communication.Proxies;
-using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
@@ -16,29 +15,27 @@ using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Runtime.Operations
{
- internal class ServiceOperation : IRepeatableOperation
+ internal class ServiceOperation : SessionOperation
{
private bool connected, mandatory;
- private IConfigurationRepository configuration;
private ILogger logger;
private IServiceProxy service;
- public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
- public event StatusChangedEventHandler StatusChanged;
+ public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
+ public override event StatusChangedEventHandler StatusChanged;
- public ServiceOperation(IConfigurationRepository configuration, ILogger logger, IServiceProxy service)
+ public ServiceOperation(ILogger logger, IServiceProxy service, SessionContext sessionContext) : base(sessionContext)
{
- this.configuration = configuration;
- this.service = service;
this.logger = logger;
+ this.service = service;
}
- public OperationResult Perform()
+ public override OperationResult Perform()
{
logger.Info($"Initializing service session...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServiceSession);
- mandatory = configuration.CurrentSettings.ServicePolicy == ServicePolicy.Mandatory;
+ mandatory = Context.Next.Settings.ServicePolicy == ServicePolicy.Mandatory;
connected = service.Connect();
if (mandatory && !connected)
@@ -59,7 +56,7 @@ namespace SafeExamBrowser.Runtime.Operations
return OperationResult.Success;
}
- public OperationResult Repeat()
+ public override OperationResult Repeat()
{
var result = Revert();
@@ -71,7 +68,7 @@ namespace SafeExamBrowser.Runtime.Operations
return Perform();
}
- public OperationResult Revert()
+ public override OperationResult Revert()
{
logger.Info("Finalizing service session...");
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServiceSession);
@@ -97,12 +94,12 @@ namespace SafeExamBrowser.Runtime.Operations
private void StartServiceSession()
{
- service.StartSession(configuration.CurrentSession.Id, configuration.CurrentSettings);
+ service.StartSession(Context.Next.Id, Context.Next.Settings);
}
private void StopServiceSession()
{
- service.StopSession(configuration.CurrentSession.Id);
+ service.StopSession(Context.Current.Id);
}
}
}
diff --git a/SafeExamBrowser.Runtime/Operations/SessionActivationOperation.cs b/SafeExamBrowser.Runtime/Operations/SessionActivationOperation.cs
new file mode 100644
index 00000000..9d68912c
--- /dev/null
+++ b/SafeExamBrowser.Runtime/Operations/SessionActivationOperation.cs
@@ -0,0 +1,63 @@
+/*
+ * 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 SafeExamBrowser.Contracts.Core.OperationModel;
+using SafeExamBrowser.Contracts.Core.OperationModel.Events;
+using SafeExamBrowser.Contracts.Logging;
+
+namespace SafeExamBrowser.Runtime.Operations
+{
+ internal class SessionActivationOperation : SessionOperation
+ {
+ private ILogger logger;
+
+ public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
+ public override event StatusChangedEventHandler StatusChanged { add { } remove { } }
+
+ public SessionActivationOperation(ILogger logger, SessionContext sessionContext) : base(sessionContext)
+ {
+ this.logger = logger;
+ }
+
+ public override OperationResult Perform()
+ {
+ ActivateNewSession();
+
+ return OperationResult.Success;
+ }
+
+ public override OperationResult Repeat()
+ {
+ ActivateNewSession();
+
+ return OperationResult.Success;
+ }
+
+ public override OperationResult Revert()
+ {
+ return OperationResult.Success;
+ }
+
+ private void ActivateNewSession()
+ {
+ var isFirstSession = Context.Current == null;
+
+ if (isFirstSession)
+ {
+ logger.Info($"Successfully activated first session '{Context.Next.Id}'.");
+ }
+ else
+ {
+ logger.Info($"Successfully terminated old session '{Context.Current.Id}' and activated new session '{Context.Next.Id}'.");
+ }
+
+ Context.Current = Context.Next;
+ Context.Next = null;
+ }
+ }
+}
diff --git a/SafeExamBrowser.Runtime/Operations/SessionInitializationOperation.cs b/SafeExamBrowser.Runtime/Operations/SessionInitializationOperation.cs
index 4c7a0a48..a60ed095 100644
--- a/SafeExamBrowser.Runtime/Operations/SessionInitializationOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/SessionInitializationOperation.cs
@@ -15,35 +15,41 @@ using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Runtime.Operations
{
- internal class SessionInitializationOperation : IRepeatableOperation
+ internal class SessionInitializationOperation : SessionOperation
{
private IConfigurationRepository configuration;
private ILogger logger;
private IRuntimeHost runtimeHost;
- public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
- public event StatusChangedEventHandler StatusChanged;
+ public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
+ public override event StatusChangedEventHandler StatusChanged;
- public SessionInitializationOperation(IConfigurationRepository configuration, ILogger logger, IRuntimeHost runtimeHost)
+ public SessionInitializationOperation(
+ IConfigurationRepository configuration,
+ ILogger logger,
+ IRuntimeHost runtimeHost,
+ SessionContext sessionContext) : base(sessionContext)
{
this.configuration = configuration;
this.logger = logger;
this.runtimeHost = runtimeHost;
}
- public OperationResult Perform()
+ public override OperationResult Perform()
{
InitializeSessionConfiguration();
return OperationResult.Success;
}
- public OperationResult Repeat()
+ public override OperationResult Repeat()
{
- return Perform();
+ InitializeSessionConfiguration();
+
+ return OperationResult.Success;
}
- public OperationResult Revert()
+ public override OperationResult Revert()
{
return OperationResult.Success;
}
@@ -53,12 +59,12 @@ namespace SafeExamBrowser.Runtime.Operations
logger.Info("Initializing new session configuration...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeSession);
- configuration.InitializeSessionConfiguration();
- runtimeHost.StartupToken = configuration.CurrentSession.StartupToken;
+ Context.Next = configuration.InitializeSessionConfiguration();
+ runtimeHost.StartupToken = Context.Next.StartupToken;
- logger.Info($" -> Client-ID: {configuration.AppConfig.ClientId}");
- logger.Info($" -> Runtime-ID: {configuration.AppConfig.RuntimeId}");
- logger.Info($" -> Session-ID: {configuration.CurrentSession.Id}");
+ logger.Info($" -> Client-ID: {Context.Next.AppConfig.ClientId}");
+ logger.Info($" -> Runtime-ID: {Context.Next.AppConfig.RuntimeId}");
+ logger.Info($" -> Session-ID: {Context.Next.Id}");
}
}
}
diff --git a/SafeExamBrowser.Runtime/Operations/SessionOperation.cs b/SafeExamBrowser.Runtime/Operations/SessionOperation.cs
new file mode 100644
index 00000000..3bdba83c
--- /dev/null
+++ b/SafeExamBrowser.Runtime/Operations/SessionOperation.cs
@@ -0,0 +1,25 @@
+using SafeExamBrowser.Contracts.Core.OperationModel;
+using SafeExamBrowser.Contracts.Core.OperationModel.Events;
+
+namespace SafeExamBrowser.Runtime.Operations
+{
+ ///
+ /// The base implementation to be used for all operations in the session operation sequence.
+ ///
+ internal abstract class SessionOperation : IRepeatableOperation
+ {
+ protected SessionContext Context { get; private set; }
+
+ public abstract event ActionRequiredEventHandler ActionRequired;
+ public abstract event StatusChangedEventHandler StatusChanged;
+
+ public SessionOperation(SessionContext sessionContext)
+ {
+ Context = sessionContext;
+ }
+
+ public abstract OperationResult Perform();
+ public abstract OperationResult Repeat();
+ public abstract OperationResult Revert();
+ }
+}
diff --git a/SafeExamBrowser.Runtime/RuntimeController.cs b/SafeExamBrowser.Runtime/RuntimeController.cs
index 0c3178b1..0a441c72 100644
--- a/SafeExamBrowser.Runtime/RuntimeController.cs
+++ b/SafeExamBrowser.Runtime/RuntimeController.cs
@@ -28,10 +28,7 @@ namespace SafeExamBrowser.Runtime
{
internal class RuntimeController : IRuntimeController
{
- private bool sessionRunning;
-
private AppConfig appConfig;
- private IConfigurationRepository configuration;
private ILogger logger;
private IMessageBox messageBox;
private IOperationSequence bootstrapSequence;
@@ -39,32 +36,43 @@ namespace SafeExamBrowser.Runtime
private IRuntimeHost runtimeHost;
private IRuntimeWindow runtimeWindow;
private IServiceProxy service;
+ private SessionContext sessionContext;
private ISplashScreen splashScreen;
private Action shutdown;
private IText text;
private IUserInterfaceFactory uiFactory;
+ private ISessionConfiguration Session
+ {
+ get { return sessionContext.Current; }
+ }
+
+ private bool SessionIsRunning
+ {
+ get { return Session != null; }
+ }
+
public RuntimeController(
AppConfig appConfig,
- IConfigurationRepository configuration,
ILogger logger,
IMessageBox messageBox,
IOperationSequence bootstrapSequence,
IRepeatableOperationSequence sessionSequence,
IRuntimeHost runtimeHost,
IServiceProxy service,
+ SessionContext sessionContext,
Action shutdown,
IText text,
IUserInterfaceFactory uiFactory)
{
this.appConfig = appConfig;
- this.configuration = configuration;
this.bootstrapSequence = bootstrapSequence;
this.logger = logger;
this.messageBox = messageBox;
this.runtimeHost = runtimeHost;
this.sessionSequence = sessionSequence;
this.service = service;
+ this.sessionContext = sessionContext;
this.shutdown = shutdown;
this.text = text;
this.uiFactory = uiFactory;
@@ -96,7 +104,7 @@ namespace SafeExamBrowser.Runtime
logger.Subscribe(runtimeWindow);
splashScreen.Close();
- StartSession(true);
+ StartSession();
}
else
{
@@ -106,14 +114,14 @@ namespace SafeExamBrowser.Runtime
messageBox.Show(TextKey.MessageBox_StartupError, TextKey.MessageBox_StartupErrorTitle, icon: MessageBoxIcon.Error, parent: splashScreen);
}
- return initialized && sessionRunning;
+ return initialized && SessionIsRunning;
}
public void Terminate()
{
DeregisterEvents();
- if (sessionRunning)
+ if (SessionIsRunning)
{
StopSession();
}
@@ -145,51 +153,79 @@ namespace SafeExamBrowser.Runtime
splashScreen.Close();
}
- private void StartSession(bool initial = false)
+ private void StartSession()
{
runtimeWindow.Show();
runtimeWindow.BringToForeground();
runtimeWindow.ShowProgressBar();
logger.Info("### --- Session Start Procedure --- ###");
- if (sessionRunning)
+ if (SessionIsRunning)
{
DeregisterSessionEvents();
}
- var result = initial ? sessionSequence.TryPerform() : sessionSequence.TryRepeat();
+ var result = SessionIsRunning ? sessionSequence.TryRepeat() : sessionSequence.TryPerform();
if (result == OperationResult.Success)
{
- RegisterSessionEvents();
-
logger.Info("### --- Session Running --- ###");
+
+ HandleSessionStartSuccess();
+ }
+ else if (result == OperationResult.Failed)
+ {
+ logger.Info("### --- Session Start Failed --- ###");
+
+ HandleSessionStartFailure();
+ }
+ else if (result == OperationResult.Aborted)
+ {
+ logger.Info("### --- Session Start Aborted --- ###");
+
+ HandleSessionStartAbortion();
+ }
+ }
+
+ private void HandleSessionStartSuccess()
+ {
+ RegisterSessionEvents();
+
+ runtimeWindow.HideProgressBar();
+ runtimeWindow.UpdateStatus(TextKey.RuntimeWindow_ApplicationRunning);
+ runtimeWindow.TopMost = Session.Settings.KioskMode != KioskMode.None;
+
+ if (Session.Settings.KioskMode == KioskMode.DisableExplorerShell)
+ {
+ runtimeWindow.Hide();
+ }
+ }
+
+ private void HandleSessionStartFailure()
+ {
+ if (SessionIsRunning)
+ {
+ StopSession();
+
+ messageBox.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error, parent: runtimeWindow);
+ logger.Info("Terminating application...");
+
+ shutdown.Invoke();
+ }
+ }
+
+ private void HandleSessionStartAbortion()
+ {
+ if (SessionIsRunning)
+ {
runtimeWindow.HideProgressBar();
runtimeWindow.UpdateStatus(TextKey.RuntimeWindow_ApplicationRunning);
- runtimeWindow.TopMost = configuration.CurrentSettings.KioskMode != KioskMode.None;
+ runtimeWindow.TopMost = Session.Settings.KioskMode != KioskMode.None;
- if (configuration.CurrentSettings.KioskMode == KioskMode.DisableExplorerShell)
+ if (Session.Settings.KioskMode == KioskMode.DisableExplorerShell)
{
runtimeWindow.Hide();
}
-
- sessionRunning = true;
- }
- else
- {
- logger.Info($"### --- Session Start {(result == OperationResult.Aborted ? "Aborted" : "Failed")} --- ###");
-
- if (result == OperationResult.Failed)
- {
- // TODO: Find solution for this; maybe manually switch back to original desktop?
- // messageBox.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error, parent: runtimeWindow);
-
- if (!initial)
- {
- logger.Info("Terminating application...");
- shutdown.Invoke();
- }
- }
}
}
@@ -207,7 +243,6 @@ namespace SafeExamBrowser.Runtime
if (success)
{
logger.Info("### --- Session Terminated --- ###");
- sessionRunning = false;
}
else
{
@@ -218,32 +253,34 @@ namespace SafeExamBrowser.Runtime
private void RegisterEvents()
{
+ runtimeHost.ClientConfigurationNeeded += RuntimeHost_ClientConfigurationNeeded;
runtimeHost.ReconfigurationRequested += RuntimeHost_ReconfigurationRequested;
runtimeHost.ShutdownRequested += RuntimeHost_ShutdownRequested;
}
private void DeregisterEvents()
{
+ runtimeHost.ClientConfigurationNeeded -= RuntimeHost_ClientConfigurationNeeded;
runtimeHost.ReconfigurationRequested -= RuntimeHost_ReconfigurationRequested;
runtimeHost.ShutdownRequested -= RuntimeHost_ShutdownRequested;
}
private void RegisterSessionEvents()
{
- configuration.CurrentSession.ClientProcess.Terminated += ClientProcess_Terminated;
- configuration.CurrentSession.ClientProxy.ConnectionLost += Client_ConnectionLost;
+ sessionContext.ClientProcess.Terminated += ClientProcess_Terminated;
+ sessionContext.ClientProxy.ConnectionLost += Client_ConnectionLost;
}
private void DeregisterSessionEvents()
{
- if (configuration.CurrentSession.ClientProcess != null)
+ if (sessionContext.ClientProcess != null)
{
- configuration.CurrentSession.ClientProcess.Terminated -= ClientProcess_Terminated;
+ sessionContext.ClientProcess.Terminated -= ClientProcess_Terminated;
}
- if (configuration.CurrentSession.ClientProxy != null)
+ if (sessionContext.ClientProxy != null)
{
- configuration.CurrentSession.ClientProxy.ConnectionLost -= Client_ConnectionLost;
+ sessionContext.ClientProxy.ConnectionLost -= Client_ConnectionLost;
}
}
@@ -261,7 +298,7 @@ namespace SafeExamBrowser.Runtime
{
logger.Error($"Client application has unexpectedly terminated with exit code {exitCode}!");
- if (sessionRunning)
+ if (SessionIsRunning)
{
StopSession();
}
@@ -275,7 +312,7 @@ namespace SafeExamBrowser.Runtime
{
logger.Error("Lost connection to the client application!");
- if (sessionRunning)
+ if (SessionIsRunning)
{
StopSession();
}
@@ -285,21 +322,31 @@ namespace SafeExamBrowser.Runtime
shutdown.Invoke();
}
+ private void RuntimeHost_ClientConfigurationNeeded(ClientConfigurationEventArgs args)
+ {
+ args.ClientConfiguration = new ClientConfiguration
+ {
+ AppConfig = sessionContext.Next.AppConfig,
+ SessionId = sessionContext.Next.Id,
+ Settings = sessionContext.Next.Settings
+ };
+ }
+
private void RuntimeHost_ReconfigurationRequested(ReconfigurationEventArgs args)
{
- var mode = configuration.CurrentSettings.ConfigurationMode;
+ var mode = Session.Settings.ConfigurationMode;
if (mode == ConfigurationMode.ConfigureClient)
{
logger.Info($"Accepted request for reconfiguration with '{args.ConfigurationPath}'.");
- configuration.ReconfigurationFilePath = args.ConfigurationPath;
+ sessionContext.ReconfigurationFilePath = args.ConfigurationPath;
StartSession();
}
else
{
logger.Info($"Denied request for reconfiguration with '{args.ConfigurationPath}' due to '{mode}' mode!");
- configuration.CurrentSession.ClientProxy.InformReconfigurationDenied(args.ConfigurationPath);
+ sessionContext.ClientProxy.InformReconfigurationDenied(args.ConfigurationPath);
}
}
@@ -333,8 +380,8 @@ namespace SafeExamBrowser.Runtime
private void AskForPassword(PasswordRequiredEventArgs args)
{
- var isStartup = configuration.CurrentSession == null;
- var isRunningOnDefaultDesktop = configuration.CurrentSettings?.KioskMode == KioskMode.DisableExplorerShell;
+ var isStartup = !SessionIsRunning;
+ var isRunningOnDefaultDesktop = SessionIsRunning && Session.Settings.KioskMode == KioskMode.DisableExplorerShell;
if (isStartup || isRunningOnDefaultDesktop)
{
@@ -374,7 +421,7 @@ namespace SafeExamBrowser.Runtime
runtimeHost.PasswordReceived += responseEventHandler;
- var communication = configuration.CurrentSession.ClientProxy.RequestPassword(args.Purpose, requestId);
+ var communication = sessionContext.ClientProxy.RequestPassword(args.Purpose, requestId);
if (communication.Success)
{
@@ -401,6 +448,9 @@ namespace SafeExamBrowser.Runtime
runtimeWindow?.UpdateStatus(status, true);
}
+ ///
+ /// TODO: Move to utility in core library and use in client controller!
+ ///
private void MapProgress(IProgressIndicator progressIndicator, ProgressChangedEventArgs args)
{
if (args.CurrentValue.HasValue)
diff --git a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj
index 71bf3f6c..baeaea2b 100644
--- a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj
+++ b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj
@@ -94,6 +94,8 @@
+
+
@@ -112,6 +114,7 @@
True
+
ResXFileCodeGenerator
Resources.Designer.cs
diff --git a/SafeExamBrowser.Runtime/SessionContext.cs b/SafeExamBrowser.Runtime/SessionContext.cs
new file mode 100644
index 00000000..ac66560e
--- /dev/null
+++ b/SafeExamBrowser.Runtime/SessionContext.cs
@@ -0,0 +1,45 @@
+/*
+ * 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 SafeExamBrowser.Contracts.Communication.Proxies;
+using SafeExamBrowser.Contracts.Configuration;
+using SafeExamBrowser.Contracts.WindowsApi;
+
+namespace SafeExamBrowser.Runtime
+{
+ ///
+ /// Holds all configuration and runtime data required for the session handling.
+ ///
+ internal class SessionContext
+ {
+ ///
+ /// The currently running client process.
+ ///
+ public IProcess ClientProcess { get; set; }
+
+ ///
+ /// The communication proxy for the currently running client process.
+ ///
+ public IClientProxy ClientProxy { get; set; }
+
+ ///
+ /// The configuration of the currently active session.
+ ///
+ public ISessionConfiguration Current { get; set; }
+
+ ///
+ /// The configuration of the next session to be activated.
+ ///
+ public ISessionConfiguration Next { get; set; }
+
+ ///
+ /// The path of the configuration file to be used for reconfiguration.
+ ///
+ public string ReconfigurationFilePath { get; set; }
+ }
+}