SEBWIN-221: Implemented scaffolding for loading and parsing of configuration resources.
This commit is contained in:
		
							parent
							
								
									b4f468a2b4
								
							
						
					
					
						commit
						902b0c2b3b
					
				
					 33 changed files with 710 additions and 174 deletions
				
			
		|  | @ -103,9 +103,17 @@ namespace SafeExamBrowser.Browser | ||||||
| 
 | 
 | ||||||
| 		private void DownloadHandler_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args) | 		private void DownloadHandler_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args) | ||||||
| 		{ | 		{ | ||||||
| 			args.BrowserWindow = window; | 			if (settings.AllowConfigurationDownloads) | ||||||
|  | 			{ | ||||||
|  | 				args.BrowserWindow = window; | ||||||
|  | 				logger.Debug($"Forwarding download request for configuration file '{fileName}'."); | ||||||
| 
 | 
 | ||||||
| 			ConfigurationDownloadRequested?.Invoke(fileName, args); | 				ConfigurationDownloadRequested?.Invoke(fileName, args); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				logger.Debug($"Discarded download request for configuration file '{fileName}'."); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private void Window_AddressChanged(string address) | 		private void Window_AddressChanged(string address) | ||||||
|  |  | ||||||
|  | @ -31,11 +31,11 @@ namespace SafeExamBrowser.Browser.Handlers | ||||||
| 
 | 
 | ||||||
| 			if (uri.Scheme == appConfig.SebUriScheme) | 			if (uri.Scheme == appConfig.SebUriScheme) | ||||||
| 			{ | 			{ | ||||||
| 				request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.ToString(); | 				request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri.AbsoluteUri; | ||||||
| 			} | 			} | ||||||
| 			else if (uri.Scheme == appConfig.SebUriSchemeSecure) | 			else if (uri.Scheme == appConfig.SebUriSchemeSecure) | ||||||
| 			{ | 			{ | ||||||
| 				request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttps }.ToString(); | 				request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttps }.Uri.AbsoluteUri; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback); | 			return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback); | ||||||
|  |  | ||||||
|  | @ -206,7 +206,7 @@ namespace SafeExamBrowser.Client | ||||||
| 		{ | 		{ | ||||||
| 			if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient) | 			if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient) | ||||||
| 			{ | 			{ | ||||||
| 				logger.Debug($"Received download request for configuration file '{fileName}'. Asking user to confirm the reconfiguration..."); | 				logger.Info($"Received download request for configuration file '{fileName}'. Asking user to confirm the reconfiguration..."); | ||||||
| 
 | 
 | ||||||
| 				var message = TextKey.MessageBox_ReconfigurationQuestion; | 				var message = TextKey.MessageBox_ReconfigurationQuestion; | ||||||
| 				var title = TextKey.MessageBox_ReconfigurationQuestionTitle; | 				var title = TextKey.MessageBox_ReconfigurationQuestionTitle; | ||||||
|  |  | ||||||
|  | @ -9,7 +9,9 @@ | ||||||
| using System; | using System; | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||||
|  | using Moq; | ||||||
| using SafeExamBrowser.Contracts.Configuration; | using SafeExamBrowser.Contracts.Configuration; | ||||||
|  | using SafeExamBrowser.Contracts.Logging; | ||||||
| 
 | 
 | ||||||
| namespace SafeExamBrowser.Configuration.UnitTests | namespace SafeExamBrowser.Configuration.UnitTests | ||||||
| { | { | ||||||
|  | @ -22,8 +24,9 @@ namespace SafeExamBrowser.Configuration.UnitTests | ||||||
| 		public void Initialize() | 		public void Initialize() | ||||||
| 		{ | 		{ | ||||||
| 			var executablePath = Assembly.GetExecutingAssembly().Location; | 			var executablePath = Assembly.GetExecutingAssembly().Location; | ||||||
|  | 			var logger = new Mock<ILogger>(); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationRepository(executablePath, string.Empty, string.Empty, string.Empty); | 			sut = new ConfigurationRepository(logger.Object, executablePath, string.Empty, string.Empty, string.Empty); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
|  |  | ||||||
|  | @ -7,7 +7,11 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| using System; | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
| using System.IO; | using System.IO; | ||||||
|  | using System.IO.Compression; | ||||||
|  | using System.Linq; | ||||||
|  | using SafeExamBrowser.Configuration.DataFormats; | ||||||
| using SafeExamBrowser.Contracts.Configuration; | using SafeExamBrowser.Contracts.Configuration; | ||||||
| using SafeExamBrowser.Contracts.Configuration.Settings; | using SafeExamBrowser.Contracts.Configuration.Settings; | ||||||
| using SafeExamBrowser.Contracts.Logging; | using SafeExamBrowser.Contracts.Logging; | ||||||
|  | @ -24,9 +28,16 @@ namespace SafeExamBrowser.Configuration | ||||||
| 		private readonly string programVersion; | 		private readonly string programVersion; | ||||||
| 
 | 
 | ||||||
| 		private AppConfig appConfig; | 		private AppConfig appConfig; | ||||||
|  | 		private IList<IDataFormat> dataFormats; | ||||||
|  | 		private ILogger logger; | ||||||
|  | 		private IList<IResourceLoader> resourceLoaders; | ||||||
| 
 | 
 | ||||||
| 		public ConfigurationRepository(string executablePath, string programCopyright, string programTitle, string programVersion) | 		public ConfigurationRepository(ILogger logger, string executablePath, string programCopyright, string programTitle, string programVersion) | ||||||
| 		{ | 		{ | ||||||
|  | 			dataFormats = new List<IDataFormat>(); | ||||||
|  | 			resourceLoaders = new List<IResourceLoader>(); | ||||||
|  | 
 | ||||||
|  | 			this.logger = logger; | ||||||
| 			this.executablePath = executablePath ?? string.Empty; | 			this.executablePath = executablePath ?? string.Empty; | ||||||
| 			this.programCopyright = programCopyright ?? string.Empty; | 			this.programCopyright = programCopyright ?? string.Empty; | ||||||
| 			this.programTitle = programTitle ?? string.Empty; | 			this.programTitle = programTitle ?? string.Empty; | ||||||
|  | @ -73,38 +84,28 @@ namespace SafeExamBrowser.Configuration | ||||||
| 
 | 
 | ||||||
| 			UpdateAppConfig(); | 			UpdateAppConfig(); | ||||||
| 
 | 
 | ||||||
| 			configuration.AppConfig = CloneAppConfig(); | 			configuration.AppConfig = appConfig.Clone(); | ||||||
| 			configuration.Id = Guid.NewGuid(); | 			configuration.Id = Guid.NewGuid(); | ||||||
| 			configuration.StartupToken = Guid.NewGuid(); | 			configuration.StartupToken = Guid.NewGuid(); | ||||||
| 
 | 
 | ||||||
| 			return configuration; | 			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() | 		public Settings LoadDefaultSettings() | ||||||
| 		{ | 		{ | ||||||
| 			// TODO: Implement default settings |  | ||||||
| 
 |  | ||||||
| 			var settings = new Settings(); | 			var settings = new Settings(); | ||||||
| 
 | 
 | ||||||
| 			settings.KioskMode = new Random().Next(10) < 5 ? KioskMode.CreateNewDesktop : KioskMode.DisableExplorerShell; | 			settings.KioskMode = KioskMode.None; | ||||||
| 			settings.ServicePolicy = ServicePolicy.Optional; | 			settings.ServicePolicy = ServicePolicy.Optional; | ||||||
| 
 | 
 | ||||||
| 			settings.Browser.StartUrl = "https://www.safeexambrowser.org/testing"; | 			settings.Browser.StartUrl = "https://www.safeexambrowser.org/testing"; | ||||||
| 			settings.Browser.AllowAddressBar = true; | 			settings.Browser.AllowAddressBar = true; | ||||||
| 			settings.Browser.AllowBackwardNavigation = true; | 			settings.Browser.AllowBackwardNavigation = true; | ||||||
|  | 			settings.Browser.AllowConfigurationDownloads = true; | ||||||
| 			settings.Browser.AllowDeveloperConsole = true; | 			settings.Browser.AllowDeveloperConsole = true; | ||||||
|  | 			settings.Browser.AllowDownloads = true; | ||||||
| 			settings.Browser.AllowForwardNavigation = true; | 			settings.Browser.AllowForwardNavigation = true; | ||||||
| 			settings.Browser.AllowReloading = true; | 			settings.Browser.AllowReloading = true; | ||||||
| 			settings.Browser.AllowDownloads = true; |  | ||||||
| 
 | 
 | ||||||
| 			settings.Taskbar.AllowApplicationLog = true; | 			settings.Taskbar.AllowApplicationLog = true; | ||||||
| 			settings.Taskbar.AllowKeyboardLayout = true; | 			settings.Taskbar.AllowKeyboardLayout = true; | ||||||
|  | @ -113,33 +114,90 @@ namespace SafeExamBrowser.Configuration | ||||||
| 			return settings; | 			return settings; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private AppConfig CloneAppConfig() | 		public void Register(IDataFormat dataFormat) | ||||||
| 		{ | 		{ | ||||||
| 			return new AppConfig | 			dataFormats.Add(dataFormat); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public void Register(IResourceLoader resourceLoader) | ||||||
|  | 		{ | ||||||
|  | 			resourceLoaders.Add(resourceLoader); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public LoadStatus TryLoadSettings(Uri resource, out Settings settings, string adminPassword = null, string settingsPassword = null) | ||||||
|  | 		{ | ||||||
|  | 			settings = default(Settings); | ||||||
|  | 
 | ||||||
|  | 			logger.Info($"Attempting to load '{resource}'..."); | ||||||
|  | 
 | ||||||
|  | 			try | ||||||
| 			{ | 			{ | ||||||
| 				AppDataFolder = appConfig.AppDataFolder, | 				var resourceLoader = resourceLoaders.FirstOrDefault(l => l.CanLoad(resource)); | ||||||
| 				ApplicationStartTime = appConfig.ApplicationStartTime, | 
 | ||||||
| 				BrowserCachePath = appConfig.BrowserCachePath, | 				if (resourceLoader != null) | ||||||
| 				BrowserLogFile = appConfig.BrowserLogFile, | 				{ | ||||||
| 				ClientAddress = appConfig.ClientAddress, | 					var data = resourceLoader.Load(resource); | ||||||
| 				ClientExecutablePath = appConfig.ClientExecutablePath, | 					var dataFormat = dataFormats.FirstOrDefault(f => f.CanParse(data)); | ||||||
| 				ClientId = appConfig.ClientId, | 
 | ||||||
| 				ClientLogFile = appConfig.ClientLogFile, | 					logger.Info($"Successfully loaded {data.Length / 1000.0} KB data from '{resource}' using {resourceLoader.GetType().Name}."); | ||||||
| 				ConfigurationFileExtension = appConfig.ConfigurationFileExtension, | 
 | ||||||
| 				DefaultSettingsFileName = appConfig.DefaultSettingsFileName, | 					if (dataFormat is HtmlFormat) | ||||||
| 				DownloadDirectory = appConfig.DownloadDirectory, | 					{ | ||||||
| 				LogLevel = appConfig.LogLevel, | 						return HandleHtml(resource, out settings); | ||||||
| 				ProgramCopyright = appConfig.ProgramCopyright, | 					} | ||||||
| 				ProgramDataFolder = appConfig.ProgramDataFolder, | 
 | ||||||
| 				ProgramTitle = appConfig.ProgramTitle, | 					if (dataFormat != null) | ||||||
| 				ProgramVersion = appConfig.ProgramVersion, | 					{ | ||||||
| 				RuntimeAddress = appConfig.RuntimeAddress, | 						return dataFormat.TryParse(data, out settings, adminPassword, settingsPassword); | ||||||
| 				RuntimeId = appConfig.RuntimeId, | 					} | ||||||
| 				RuntimeLogFile = appConfig.RuntimeLogFile, | 				} | ||||||
| 				SebUriScheme = appConfig.SebUriScheme, | 
 | ||||||
| 				SebUriSchemeSecure = appConfig.SebUriSchemeSecure, | 				logger.Warn($"No {(resourceLoader == null ? "resource loader" : "data format")} found for '{resource}'!"); | ||||||
| 				ServiceAddress = appConfig.ServiceAddress | 
 | ||||||
| 			}; | 				return LoadStatus.NotSupported; | ||||||
|  | 			} | ||||||
|  | 			catch (Exception e) | ||||||
|  | 			{ | ||||||
|  | 				logger.Error($"Unexpected error while trying to load '{resource}'!", e); | ||||||
|  | 
 | ||||||
|  | 				return LoadStatus.UnexpectedError; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private LoadStatus HandleHtml(Uri resource, out Settings settings) | ||||||
|  | 		{ | ||||||
|  | 			logger.Info($"Loaded data appears to be HTML, loading default settings and using '{resource}' as startup URL."); | ||||||
|  | 
 | ||||||
|  | 			settings = LoadDefaultSettings(); | ||||||
|  | 			settings.Browser.StartUrl = resource.AbsoluteUri; | ||||||
|  | 
 | ||||||
|  | 			return LoadStatus.Success; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private byte[] Decompress(byte[] bytes) | ||||||
|  | 		{ | ||||||
|  | 			try | ||||||
|  | 			{ | ||||||
|  | 				var buffer = new byte[4096]; | ||||||
|  | 
 | ||||||
|  | 				using (var stream = new GZipStream(new MemoryStream(bytes), CompressionMode.Decompress)) | ||||||
|  | 				using (var decompressed = new MemoryStream()) | ||||||
|  | 				{ | ||||||
|  | 					var bytesRead = 0; | ||||||
|  | 
 | ||||||
|  | 					do | ||||||
|  | 					{ | ||||||
|  | 						bytesRead = stream.Read(buffer, 0, buffer.Length); | ||||||
|  | 						decompressed.Write(buffer, 0, bytesRead); | ||||||
|  | 					} while (bytesRead > 0); | ||||||
|  | 
 | ||||||
|  | 					return decompressed.ToArray(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			catch (InvalidDataException) | ||||||
|  | 			{ | ||||||
|  | 				return bytes; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private void UpdateAppConfig() | 		private void UpdateAppConfig() | ||||||
|  |  | ||||||
							
								
								
									
										37
									
								
								SafeExamBrowser.Configuration/DataFormats/DefaultFormat.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								SafeExamBrowser.Configuration/DataFormats/DefaultFormat.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | /* | ||||||
|  |  * 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; | ||||||
|  | using SafeExamBrowser.Contracts.Configuration.Settings; | ||||||
|  | using SafeExamBrowser.Contracts.Logging; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Configuration.DataFormats | ||||||
|  | { | ||||||
|  | 	public class DefaultFormat : IDataFormat | ||||||
|  | 	{ | ||||||
|  | 		private ILogger logger; | ||||||
|  | 
 | ||||||
|  | 		public DefaultFormat(ILogger logger) | ||||||
|  | 		{ | ||||||
|  | 			this.logger = logger; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public bool CanParse(byte[] data) | ||||||
|  | 		{ | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public LoadStatus TryParse(byte[] data, out Settings settings, string adminPassword = null, string settingsPassword = null) | ||||||
|  | 		{ | ||||||
|  | 			settings = new Settings(); | ||||||
|  | 			settings.ServicePolicy = ServicePolicy.Optional; | ||||||
|  | 
 | ||||||
|  | 			return LoadStatus.Success; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								SafeExamBrowser.Configuration/DataFormats/HtmlFormat.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								SafeExamBrowser.Configuration/DataFormats/HtmlFormat.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | ||||||
|  | /* | ||||||
|  |  * 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; | ||||||
|  | using SafeExamBrowser.Contracts.Configuration.Settings; | ||||||
|  | using SafeExamBrowser.Contracts.Logging; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Configuration.DataFormats | ||||||
|  | { | ||||||
|  | 	public class HtmlFormat : IDataFormat | ||||||
|  | 	{ | ||||||
|  | 		private ILogger logger; | ||||||
|  | 
 | ||||||
|  | 		public HtmlFormat(ILogger logger) | ||||||
|  | 		{ | ||||||
|  | 			this.logger = logger; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public bool CanParse(byte[] data) | ||||||
|  | 		{ | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public LoadStatus TryParse(byte[] data, out Settings settings, string adminPassword = null, string settingsPassword = null) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.NotImplementedException(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								SafeExamBrowser.Configuration/DataFormats/XmlFormat.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								SafeExamBrowser.Configuration/DataFormats/XmlFormat.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | ||||||
|  | /* | ||||||
|  |  * 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; | ||||||
|  | using SafeExamBrowser.Contracts.Configuration.Settings; | ||||||
|  | using SafeExamBrowser.Contracts.Logging; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Configuration.DataFormats | ||||||
|  | { | ||||||
|  | 	public class XmlFormat : IDataFormat | ||||||
|  | 	{ | ||||||
|  | 		private ILogger logger; | ||||||
|  | 
 | ||||||
|  | 		public XmlFormat(ILogger logger) | ||||||
|  | 		{ | ||||||
|  | 			this.logger = logger; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public bool CanParse(byte[] data) | ||||||
|  | 		{ | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public LoadStatus TryParse(byte[] data, out Settings settings, string adminPassword = null, string settingsPassword = null) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.NotImplementedException(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,22 +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.Configuration; |  | ||||||
| 
 |  | ||||||
| namespace SafeExamBrowser.Configuration |  | ||||||
| { |  | ||||||
| 	public class ResourceLoader : IResourceLoader |  | ||||||
| 	{ |  | ||||||
| 		public bool IsHtmlResource(Uri resource) |  | ||||||
| 		{ |  | ||||||
| 			// TODO: Implement resource loader |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,46 @@ | ||||||
|  | /* | ||||||
|  |  * 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 System.IO; | ||||||
|  | using SafeExamBrowser.Contracts.Configuration; | ||||||
|  | using SafeExamBrowser.Contracts.Logging; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Configuration.ResourceLoaders | ||||||
|  | { | ||||||
|  | 	public class FileResourceLoader : IResourceLoader | ||||||
|  | 	{ | ||||||
|  | 		private ILogger logger; | ||||||
|  | 
 | ||||||
|  | 		public FileResourceLoader(ILogger logger) | ||||||
|  | 		{ | ||||||
|  | 			this.logger = logger; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public bool CanLoad(Uri resource) | ||||||
|  | 		{ | ||||||
|  | 			if (resource.IsFile && File.Exists(resource.AbsolutePath)) | ||||||
|  | 			{ | ||||||
|  | 				logger.Debug($"Can load '{resource}' as it references an existing file."); | ||||||
|  | 
 | ||||||
|  | 				return true; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			logger.Debug($"Can't load '{resource}' since it isn't a file URI or no file exists at the specified path."); | ||||||
|  | 
 | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public byte[] Load(Uri resource) | ||||||
|  | 		{ | ||||||
|  | 			logger.Debug($"Loading data from '{resource}'..."); | ||||||
|  | 
 | ||||||
|  | 			return File.ReadAllBytes(resource.AbsolutePath); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,135 @@ | ||||||
|  | /* | ||||||
|  |  * 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 System.Linq; | ||||||
|  | using System.Net.Http; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | using SafeExamBrowser.Contracts.Configuration; | ||||||
|  | using SafeExamBrowser.Contracts.Logging; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Configuration.ResourceLoaders | ||||||
|  | { | ||||||
|  | 	public class NetworkResourceLoader : IResourceLoader | ||||||
|  | 	{ | ||||||
|  | 		private AppConfig appConfig; | ||||||
|  | 		private ILogger logger; | ||||||
|  | 
 | ||||||
|  | 		private string[] SupportedSchemes => new[] | ||||||
|  | 		{ | ||||||
|  | 			appConfig.SebUriScheme, | ||||||
|  | 			appConfig.SebUriSchemeSecure, | ||||||
|  | 			Uri.UriSchemeHttp, | ||||||
|  | 			Uri.UriSchemeHttps | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		public NetworkResourceLoader(AppConfig appConfig, ILogger logger) | ||||||
|  | 		{ | ||||||
|  | 			this.appConfig = appConfig; | ||||||
|  | 			this.logger = logger; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public bool CanLoad(Uri resource) | ||||||
|  | 		{ | ||||||
|  | 			if (SupportedSchemes.Contains(resource.Scheme) && IsAvailable(resource)) | ||||||
|  | 			{ | ||||||
|  | 				logger.Debug($"Can load '{resource}' as it references an existing network resource."); | ||||||
|  | 
 | ||||||
|  | 				return true; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			logger.Debug($"Can't load '{resource}' since its URI scheme is not supported or the resource is unavailable."); | ||||||
|  | 
 | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public byte[] Load(Uri resource) | ||||||
|  | 		{ | ||||||
|  | 			var uri = BuildUriFor(resource); | ||||||
|  | 
 | ||||||
|  | 			logger.Debug($"Downloading data from '{uri}'..."); | ||||||
|  | 
 | ||||||
|  | 			var request = new HttpRequestMessage(HttpMethod.Get, uri); | ||||||
|  | 			var response = Execute(request); | ||||||
|  | 
 | ||||||
|  | 			logger.Debug($"Sent GET request for '{uri}', received response '{(int) response.StatusCode} - {response.ReasonPhrase}'."); | ||||||
|  | 
 | ||||||
|  | 			var data = Extract(response.Content); | ||||||
|  | 
 | ||||||
|  | 			logger.Debug($"Extracted {data.Length / 1000.0} KB data from response."); | ||||||
|  | 
 | ||||||
|  | 			return data; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private bool IsAvailable(Uri resource) | ||||||
|  | 		{ | ||||||
|  | 			try | ||||||
|  | 			{ | ||||||
|  | 				var uri = BuildUriFor(resource); | ||||||
|  | 				var request = new HttpRequestMessage(HttpMethod.Head, uri); | ||||||
|  | 				var response = Execute(request); | ||||||
|  | 
 | ||||||
|  | 				logger.Debug($"Sent HEAD request for '{uri}', received response '{(int) response.StatusCode} - {response.ReasonPhrase}'."); | ||||||
|  | 
 | ||||||
|  | 				return response.IsSuccessStatusCode; | ||||||
|  | 			} | ||||||
|  | 			catch (Exception e) | ||||||
|  | 			{ | ||||||
|  | 				logger.Error($"Failed to check availability of '{resource}'!", e); | ||||||
|  | 
 | ||||||
|  | 				return false; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private Uri BuildUriFor(Uri resource) | ||||||
|  | 		{ | ||||||
|  | 			var scheme = GetSchemeFor(resource); | ||||||
|  | 			var builder = new UriBuilder(resource) { Scheme = scheme }; | ||||||
|  | 
 | ||||||
|  | 			return builder.Uri; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private string GetSchemeFor(Uri resource) | ||||||
|  | 		{ | ||||||
|  | 			if (resource.Scheme == appConfig.SebUriScheme) | ||||||
|  | 			{ | ||||||
|  | 				return Uri.UriSchemeHttp; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (resource.Scheme == appConfig.SebUriSchemeSecure) | ||||||
|  | 			{ | ||||||
|  | 				return Uri.UriSchemeHttps; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return resource.Scheme; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private HttpResponseMessage Execute(HttpRequestMessage request) | ||||||
|  | 		{ | ||||||
|  | 			var task = Task.Run(async () => | ||||||
|  | 			{ | ||||||
|  | 				using (var client = new HttpClient()) | ||||||
|  | 				{ | ||||||
|  | 					return await client.SendAsync(request); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			return task.GetAwaiter().GetResult(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private byte[] Extract(HttpContent content) | ||||||
|  | 		{ | ||||||
|  | 			var task = Task.Run(async () => | ||||||
|  | 			{ | ||||||
|  | 				return await content.ReadAsByteArrayAsync(); | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			return task.GetAwaiter().GetResult(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -48,13 +48,18 @@ | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Reference Include="System" /> |     <Reference Include="System" /> | ||||||
|  |     <Reference Include="System.Net.Http" /> | ||||||
|     <Reference Include="System.Windows.Forms" /> |     <Reference Include="System.Windows.Forms" /> | ||||||
|     <Reference Include="Microsoft.CSharp" /> |     <Reference Include="Microsoft.CSharp" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Compile Include="ResourceLoader.cs" /> |     <Compile Include="DataFormats\DefaultFormat.cs" /> | ||||||
|  |     <Compile Include="DataFormats\HtmlFormat.cs" /> | ||||||
|  |     <Compile Include="DataFormats\XmlFormat.cs" /> | ||||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> |     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||||
|     <Compile Include="ConfigurationRepository.cs" /> |     <Compile Include="ConfigurationRepository.cs" /> | ||||||
|  |     <Compile Include="ResourceLoaders\FileResourceLoader.cs" /> | ||||||
|  |     <Compile Include="ResourceLoaders\NetworkResourceLoader.cs" /> | ||||||
|     <Compile Include="SessionConfiguration.cs" /> |     <Compile Include="SessionConfiguration.cs" /> | ||||||
|     <Compile Include="SystemInfo.cs" /> |     <Compile Include="SystemInfo.cs" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
|  | @ -126,5 +126,13 @@ namespace SafeExamBrowser.Contracts.Configuration | ||||||
| 		/// The communication address of the service component. | 		/// The communication address of the service component. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public string ServiceAddress { get; set; } | 		public string ServiceAddress { get; set; } | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a shallow clone. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public AppConfig Clone() | ||||||
|  | 		{ | ||||||
|  | 			return MemberwiseClone() as AppConfig; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -25,16 +25,25 @@ namespace SafeExamBrowser.Contracts.Configuration | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		ISessionConfiguration InitializeSessionConfiguration(); | 		ISessionConfiguration InitializeSessionConfiguration(); | ||||||
| 
 | 
 | ||||||
| 		/// <summary> |  | ||||||
| 		/// Attempts to load settings from the specified resource, using the optional passwords. Returns a <see cref="LoadStatus"/> |  | ||||||
| 		/// indicating the result of the operation. As long as the result is not <see cref="LoadStatus.Success"/>, the declared |  | ||||||
| 		/// <paramref name="settings"/> will be <c>null</c>! |  | ||||||
| 		/// </summary> |  | ||||||
| 		LoadStatus TryLoadSettings(Uri resource, out Settings.Settings settings, string adminPassword = null, string settingsPassword = null); |  | ||||||
| 
 |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Loads the default settings. | 		/// Loads the default settings. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		Settings.Settings LoadDefaultSettings(); | 		Settings.Settings LoadDefaultSettings(); | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Registers the specified <see cref="IDataFormat"/> as option to parse configuration data. | ||||||
|  | 		/// </summary> | ||||||
|  | 		void Register(IDataFormat dataFormat); | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Registers the specified <see cref="IResourceLoader"/> as option to load configuration data. | ||||||
|  | 		/// </summary> | ||||||
|  | 		void Register(IResourceLoader resourceLoader); | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Attempts to load settings from the specified resource, using the optional passwords. As long as the result is not | ||||||
|  | 		/// <see cref="LoadStatus.Success"/>, the referenced settings may be <c>null</c> or in an undefinable state! | ||||||
|  | 		/// </summary> | ||||||
|  | 		LoadStatus TryLoadSettings(Uri resource, out Settings.Settings settings, string adminPassword = null, string settingsPassword = null); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										26
									
								
								SafeExamBrowser.Contracts/Configuration/IDataFormat.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								SafeExamBrowser.Contracts/Configuration/IDataFormat.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -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/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Contracts.Configuration | ||||||
|  | { | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Defines the data format for a configuration file. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public interface IDataFormat | ||||||
|  | 	{ | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Indicates whether the given data complies with the required format. | ||||||
|  | 		/// </summary> | ||||||
|  | 		bool CanParse(byte[] data); | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Attempts to parse the given binary data. | ||||||
|  | 		/// </summary> | ||||||
|  | 		LoadStatus TryParse(byte[] data, out Settings.Settings settings, string adminPassword = null, string settingsPassword = null); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -11,13 +11,18 @@ using System; | ||||||
| namespace SafeExamBrowser.Contracts.Configuration | namespace SafeExamBrowser.Contracts.Configuration | ||||||
| { | { | ||||||
| 	/// <summary> | 	/// <summary> | ||||||
| 	/// Loads configuration data from various sources (e.g. the internet) and provides related resource handling functionality. | 	/// Loads binary data from a particular resource. | ||||||
| 	/// </summary> | 	/// </summary> | ||||||
| 	public interface IResourceLoader | 	public interface IResourceLoader | ||||||
| 	{ | 	{ | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Indicates whether the given <see cref="Uri"/> identifies a HTML resource. | 		/// Indicates whether the resource loader is able to load data from the specified resource. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		bool IsHtmlResource(Uri resource); | 		bool CanLoad(Uri resource); | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Loads the binary data from the specified resource. | ||||||
|  | 		/// </summary> | ||||||
|  | 		byte[] Load(Uri resource); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ | ||||||
| namespace SafeExamBrowser.Contracts.Configuration | namespace SafeExamBrowser.Contracts.Configuration | ||||||
| { | { | ||||||
| 	/// <summary> | 	/// <summary> | ||||||
| 	/// Defines all possible results of <see cref="IConfigurationRepository.LoadSettings(System.Uri)"/>. | 	/// Defines all possible results of an attempt to load a configuration file. | ||||||
| 	/// </summary> | 	/// </summary> | ||||||
| 	public enum LoadStatus | 	public enum LoadStatus | ||||||
| 	{ | 	{ | ||||||
|  | @ -23,14 +23,24 @@ namespace SafeExamBrowser.Contracts.Configuration | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		InvalidData, | 		InvalidData, | ||||||
| 
 | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Indicates that a resource is not supported. | ||||||
|  | 		/// </summary> | ||||||
|  | 		NotSupported, | ||||||
|  | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Indicates that a settings password is needed in order to load the settings. | 		/// Indicates that a settings password is needed in order to load the settings. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		SettingsPasswordNeeded, | 		SettingsPasswordNeeded, | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// The <see cref="Settings.Settings"/> were loaded successfully. | 		/// The settings were loaded successfully. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		Success | 		Success, | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// An unexpected error occurred while trying to load the settings. | ||||||
|  | 		/// </summary> | ||||||
|  | 		UnexpectedError | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -26,13 +26,18 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public bool AllowBackwardNavigation { get; set; } | 		public bool AllowBackwardNavigation { get; set; } | ||||||
| 
 | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Determines whether the user should be allowed to download configuration files. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public bool AllowConfigurationDownloads { get; set; } | ||||||
|  | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Determines whether the user should be allowed to open the developer console of a browser window. | 		/// Determines whether the user should be allowed to open the developer console of a browser window. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public bool AllowDeveloperConsole { get; set; } | 		public bool AllowDeveloperConsole { get; set; } | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Determines whether the user should be allowed to download files. | 		/// Determines whether the user should be allowed to download files (excluding configuration files). | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public bool AllowDownloads { get; set; } | 		public bool AllowDownloads { get; set; } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,6 +22,10 @@ namespace SafeExamBrowser.Contracts.I18n | ||||||
| 		MessageBox_ClientConfigurationQuestionTitle, | 		MessageBox_ClientConfigurationQuestionTitle, | ||||||
| 		MessageBox_ConfigurationDownloadError, | 		MessageBox_ConfigurationDownloadError, | ||||||
| 		MessageBox_ConfigurationDownloadErrorTitle, | 		MessageBox_ConfigurationDownloadErrorTitle, | ||||||
|  | 		MessageBox_InvalidConfigurationData, | ||||||
|  | 		MessageBox_InvalidConfigurationDataTitle, | ||||||
|  | 		MessageBox_NotSupportedConfigurationResource, | ||||||
|  | 		MessageBox_NotSupportedConfigurationResourceTitle, | ||||||
| 		MessageBox_Quit, | 		MessageBox_Quit, | ||||||
| 		MessageBox_QuitTitle, | 		MessageBox_QuitTitle, | ||||||
| 		MessageBox_QuitError, | 		MessageBox_QuitError, | ||||||
|  | @ -42,6 +46,8 @@ namespace SafeExamBrowser.Contracts.I18n | ||||||
| 		MessageBox_SingleInstanceTitle, | 		MessageBox_SingleInstanceTitle, | ||||||
| 		MessageBox_StartupError, | 		MessageBox_StartupError, | ||||||
| 		MessageBox_StartupErrorTitle, | 		MessageBox_StartupErrorTitle, | ||||||
|  | 		MessageBox_UnexpectedConfigurationError, | ||||||
|  | 		MessageBox_UnexpectedConfigurationErrorTitle, | ||||||
| 		Notification_AboutTooltip, | 		Notification_AboutTooltip, | ||||||
| 		Notification_LogTooltip, | 		Notification_LogTooltip, | ||||||
| 		OperationStatus_CloseRuntimeConnection, | 		OperationStatus_CloseRuntimeConnection, | ||||||
|  |  | ||||||
|  | @ -53,6 +53,7 @@ | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Compile Include="Communication\Events\ClientConfigurationEventArgs.cs" /> |     <Compile Include="Communication\Events\ClientConfigurationEventArgs.cs" /> | ||||||
|  |     <Compile Include="Configuration\IDataFormat.cs" /> | ||||||
|     <Compile Include="Core\Events\InstanceTerminatedEventHandler.cs" /> |     <Compile Include="Core\Events\InstanceTerminatedEventHandler.cs" /> | ||||||
|     <Compile Include="Core\Events\NameChangedEventHandler.cs" /> |     <Compile Include="Core\Events\NameChangedEventHandler.cs" /> | ||||||
|     <Compile Include="Core\IApplicationController.cs" /> |     <Compile Include="Core\IApplicationController.cs" /> | ||||||
|  |  | ||||||
|  | @ -24,6 +24,18 @@ | ||||||
|   <Entry key="MessageBox_ConfigurationDownloadErrorTitle"> |   <Entry key="MessageBox_ConfigurationDownloadErrorTitle"> | ||||||
|     Download Error |     Download Error | ||||||
|   </Entry> |   </Entry> | ||||||
|  |   <Entry key="MessageBox_InvalidConfigurationData"> | ||||||
|  |     The configuration resource '%%URI%%' contains invalid data! | ||||||
|  |   </Entry> | ||||||
|  |   <Entry key="MessageBox_InvalidConfigurationDataTitle"> | ||||||
|  |     Configuration Error | ||||||
|  |   </Entry> | ||||||
|  |   <Entry key="MessageBox_NotSupportedConfigurationResource"> | ||||||
|  |     The configuration resource '%%URI%%' is not supported! | ||||||
|  |   </Entry> | ||||||
|  |   <Entry key="MessageBox_NotSupportedConfigurationResourceTitle"> | ||||||
|  |     Configuration Error | ||||||
|  |   </Entry> | ||||||
|   <Entry key="MessageBox_Quit"> |   <Entry key="MessageBox_Quit"> | ||||||
|     Would you really like to quit the application? |     Would you really like to quit the application? | ||||||
|   </Entry> |   </Entry> | ||||||
|  | @ -78,6 +90,12 @@ | ||||||
|   <Entry key="MessageBox_StartupErrorTitle"> |   <Entry key="MessageBox_StartupErrorTitle"> | ||||||
|     Startup Error |     Startup Error | ||||||
|   </Entry> |   </Entry> | ||||||
|  |   <Entry key="MessageBox_UnexpectedConfigurationError"> | ||||||
|  |     An unexpected error occurred while trying to load configuration resource '%%URI%%'! Please consult the application log for more information... | ||||||
|  |   </Entry> | ||||||
|  |   <Entry key="MessageBox_UnexpectedConfigurationErrorTitle"> | ||||||
|  |     Configuration Error | ||||||
|  |   </Entry> | ||||||
|   <Entry key="Notification_AboutTooltip"> |   <Entry key="Notification_AboutTooltip"> | ||||||
|     About Safe Exam Browser |     About Safe Exam Browser | ||||||
|   </Entry> |   </Entry> | ||||||
|  |  | ||||||
|  | @ -26,7 +26,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 		private AppConfig appConfig; | 		private AppConfig appConfig; | ||||||
| 		private Mock<ILogger> logger; | 		private Mock<ILogger> logger; | ||||||
| 		private Mock<IConfigurationRepository> repository; | 		private Mock<IConfigurationRepository> repository; | ||||||
| 		private Mock<IResourceLoader> resourceLoader; |  | ||||||
| 		private Mock<ISessionConfiguration> session; | 		private Mock<ISessionConfiguration> session; | ||||||
| 		private SessionContext sessionContext; | 		private SessionContext sessionContext; | ||||||
| 		private Settings settings; | 		private Settings settings; | ||||||
|  | @ -39,7 +38,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 			appConfig = new AppConfig(); | 			appConfig = new AppConfig(); | ||||||
| 			logger = new Mock<ILogger>(); | 			logger = new Mock<ILogger>(); | ||||||
| 			repository = new Mock<IConfigurationRepository>(); | 			repository = new Mock<IConfigurationRepository>(); | ||||||
| 			resourceLoader = new Mock<IResourceLoader>(); |  | ||||||
| 			session = new Mock<ISessionConfiguration>(); | 			session = new Mock<ISessionConfiguration>(); | ||||||
| 			sessionContext = new SessionContext(); | 			sessionContext = new SessionContext(); | ||||||
| 			settings = new Settings(); | 			settings = new Settings(); | ||||||
|  | @ -64,7 +62,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 
 | 
 | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.Perform(); | 			sut.Perform(); | ||||||
| 
 | 
 | ||||||
| 			var resource = new Uri(url); | 			var resource = new Uri(url); | ||||||
|  | @ -82,7 +80,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 
 | 
 | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.Perform(); | 			sut.Perform(); | ||||||
| 
 | 
 | ||||||
| 			var resource = new Uri(Path.Combine(location, "SettingsDummy.txt")); | 			var resource = new Uri(Path.Combine(location, "SettingsDummy.txt")); | ||||||
|  | @ -99,7 +97,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 
 | 
 | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.Perform(); | 			sut.Perform(); | ||||||
| 
 | 
 | ||||||
| 			var resource = new Uri(Path.Combine(location, "SettingsDummy.txt")); | 			var resource = new Uri(Path.Combine(location, "SettingsDummy.txt")); | ||||||
|  | @ -110,10 +108,19 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void MustFallbackToDefaultsAsLastPrio() | 		public void MustFallbackToDefaultsAsLastPrio() | ||||||
| 		{ | 		{ | ||||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			var actualSettings = default(Settings); | ||||||
|  | 			var defaultSettings = new Settings(); | ||||||
|  | 
 | ||||||
|  | 			repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings); | ||||||
|  | 			session.SetupSet<Settings>(s => s.Settings = It.IsAny<Settings>()).Callback(s => actualSettings = s); | ||||||
|  | 
 | ||||||
|  | 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.Perform(); | 			sut.Perform(); | ||||||
| 
 | 
 | ||||||
| 			repository.Verify(r => r.LoadDefaultSettings(), Times.Once); | 			repository.Verify(r => r.LoadDefaultSettings(), Times.Once); | ||||||
|  | 			session.VerifySet(s => s.Settings = defaultSettings); | ||||||
|  | 
 | ||||||
|  | 			Assert.AreSame(defaultSettings, actualSettings); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
|  | @ -122,7 +129,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 			appConfig.ProgramDataFolder = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations)); | 			appConfig.ProgramDataFolder = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations)); | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is ConfigurationCompletedEventArgs c) | 				if (args is ConfigurationCompletedEventArgs c) | ||||||
|  | @ -141,7 +148,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 		{ | 		{ | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is ConfigurationCompletedEventArgs c) | 				if (args is ConfigurationCompletedEventArgs c) | ||||||
|  | @ -161,7 +168,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 			settings.ConfigurationMode = ConfigurationMode.Exam; | 			settings.ConfigurationMode = ConfigurationMode.Exam; | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is ConfigurationCompletedEventArgs c) | 				if (args is ConfigurationCompletedEventArgs c) | ||||||
|  | @ -176,15 +183,25 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void MustNotFailWithoutCommandLineArgs() | 		public void MustNotFailWithoutCommandLineArgs() | ||||||
| 		{ | 		{ | ||||||
| 			repository.Setup(r => r.LoadDefaultSettings()); | 			var actualSettings = default(Settings); | ||||||
|  | 			var defaultSettings = new Settings(); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings); | ||||||
|  | 			session.SetupSet<Settings>(s => s.Settings = It.IsAny<Settings>()).Callback(s => actualSettings = s); | ||||||
|  | 
 | ||||||
|  | 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.Perform(); | 			sut.Perform(); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new string[] { }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			repository.Verify(r => r.LoadDefaultSettings(), Times.Once); | ||||||
|  | 
 | ||||||
|  | 			Assert.AreSame(defaultSettings, actualSettings); | ||||||
|  | 
 | ||||||
|  | 			sut = new ConfigurationOperation(new string[] { }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.Perform(); | 			sut.Perform(); | ||||||
| 
 | 
 | ||||||
| 			repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2)); | 			repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2)); | ||||||
|  | 
 | ||||||
|  | 			Assert.AreSame(defaultSettings, actualSettings); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
|  | @ -192,7 +209,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 		{ | 		{ | ||||||
| 			var uri = @"an/invalid\uri.'*%yolo/()你好"; | 			var uri = @"an/invalid\uri.'*%yolo/()你好"; | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", uri }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(new[] { "blubb.exe", uri }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.Perform(); | 			sut.Perform(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -203,7 +220,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 
 | 
 | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is PasswordRequiredEventArgs p) | 				if (args is PasswordRequiredEventArgs p) | ||||||
|  | @ -224,7 +241,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 
 | 
 | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is PasswordRequiredEventArgs p) | 				if (args is PasswordRequiredEventArgs p) | ||||||
|  | @ -247,7 +264,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded); | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password, null)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password, null)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is PasswordRequiredEventArgs p) | 				if (args is PasswordRequiredEventArgs p) | ||||||
|  | @ -272,7 +289,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded); | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, password)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, password)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is PasswordRequiredEventArgs p) | 				if (args is PasswordRequiredEventArgs p) | ||||||
|  | @ -295,7 +312,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 
 | 
 | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is PasswordRequiredEventArgs p) | 				if (args is PasswordRequiredEventArgs p) | ||||||
|  | @ -316,7 +333,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 
 | 
 | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is PasswordRequiredEventArgs p) | 				if (args is PasswordRequiredEventArgs p) | ||||||
|  | @ -341,7 +358,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, settingsPassword)).Returns(LoadStatus.AdminPasswordNeeded); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, settingsPassword)).Returns(LoadStatus.AdminPasswordNeeded); | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, adminPassword, settingsPassword)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, adminPassword, settingsPassword)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext); | ||||||
| 			sut.ActionRequired += args => | 			sut.ActionRequired += args => | ||||||
| 			{ | 			{ | ||||||
| 				if (args is PasswordRequiredEventArgs p) | 				if (args is PasswordRequiredEventArgs p) | ||||||
|  | @ -358,37 +375,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 			repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, adminPassword, settingsPassword), Times.Once); | 			repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, adminPassword, settingsPassword), Times.Once); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		[TestMethod] |  | ||||||
| 		public void MustHandleInvalidData() |  | ||||||
| 		{ |  | ||||||
| 			var url = @"http://www.safeexambrowser.org/whatever.seb"; |  | ||||||
| 
 |  | ||||||
| 			resourceLoader.Setup(r => r.IsHtmlResource(It.IsAny<Uri>())).Returns(false); |  | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.InvalidData); |  | ||||||
| 
 |  | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); |  | ||||||
| 
 |  | ||||||
| 			var result = sut.Perform(); |  | ||||||
| 
 |  | ||||||
| 			Assert.AreEqual(OperationResult.Failed, result); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		[TestMethod] |  | ||||||
| 		public void MustHandleHtmlAsInvalidData() |  | ||||||
| 		{ |  | ||||||
| 			var url = "http://www.blubb.org/some/resource.html"; |  | ||||||
| 
 |  | ||||||
| 			resourceLoader.Setup(r => r.IsHtmlResource(It.IsAny<Uri>())).Returns(true); |  | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.InvalidData); |  | ||||||
| 
 |  | ||||||
| 			sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext); |  | ||||||
| 
 |  | ||||||
| 			var result = sut.Perform(); |  | ||||||
| 
 |  | ||||||
| 			Assert.AreEqual(OperationResult.Success, result); |  | ||||||
| 			Assert.AreEqual(url, settings.Browser.StartUrl); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void MustReconfigureSuccessfullyWithCorrectUri() | 		public void MustReconfigureSuccessfullyWithCorrectUri() | ||||||
| 		{ | 		{ | ||||||
|  | @ -398,7 +384,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 			sessionContext.ReconfigurationFilePath = resource.AbsolutePath; | 			sessionContext.ReconfigurationFilePath = resource.AbsolutePath; | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null, null)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null, null)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||||
| 
 | 
 | ||||||
| 			var result = sut.Repeat(); | 			var result = sut.Repeat(); | ||||||
| 
 | 
 | ||||||
|  | @ -415,7 +401,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | ||||||
| 			sessionContext.ReconfigurationFilePath = null; | 			sessionContext.ReconfigurationFilePath = null; | ||||||
| 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | 			repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success); | ||||||
| 
 | 
 | ||||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||||
| 
 | 
 | ||||||
| 			var result = sut.Repeat(); | 			var result = sut.Repeat(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,6 +6,10 @@ | ||||||
|         <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> |         <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> | ||||||
|         <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" /> |         <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" /> | ||||||
|       </dependentAssembly> |       </dependentAssembly> | ||||||
|  |       <dependentAssembly> | ||||||
|  |         <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> | ||||||
|  |         <bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" /> | ||||||
|  |       </dependentAssembly> | ||||||
|     </assemblyBinding> |     </assemblyBinding> | ||||||
|   </runtime> |   </runtime> | ||||||
| </configuration> | </configuration> | ||||||
|  | @ -13,6 +13,8 @@ using System.Reflection; | ||||||
| using SafeExamBrowser.Communication.Hosts; | using SafeExamBrowser.Communication.Hosts; | ||||||
| using SafeExamBrowser.Communication.Proxies; | using SafeExamBrowser.Communication.Proxies; | ||||||
| using SafeExamBrowser.Configuration; | using SafeExamBrowser.Configuration; | ||||||
|  | using SafeExamBrowser.Configuration.DataFormats; | ||||||
|  | using SafeExamBrowser.Configuration.ResourceLoaders; | ||||||
| using SafeExamBrowser.Contracts.Configuration; | using SafeExamBrowser.Contracts.Configuration; | ||||||
| using SafeExamBrowser.Contracts.Core; | using SafeExamBrowser.Contracts.Core; | ||||||
| using SafeExamBrowser.Contracts.Core.OperationModel; | using SafeExamBrowser.Contracts.Core.OperationModel; | ||||||
|  | @ -32,6 +34,7 @@ namespace SafeExamBrowser.Runtime | ||||||
| 	internal class CompositionRoot | 	internal class CompositionRoot | ||||||
| 	{ | 	{ | ||||||
| 		private AppConfig appConfig; | 		private AppConfig appConfig; | ||||||
|  | 		private IConfigurationRepository configuration; | ||||||
| 		private ILogger logger; | 		private ILogger logger; | ||||||
| 		private ISystemInfo systemInfo; | 		private ISystemInfo systemInfo; | ||||||
| 		private IText text; | 		private IText text; | ||||||
|  | @ -45,13 +48,12 @@ namespace SafeExamBrowser.Runtime | ||||||
| 			const int FIFTEEN_SECONDS = 15000; | 			const int FIFTEEN_SECONDS = 15000; | ||||||
| 
 | 
 | ||||||
| 			var args = Environment.GetCommandLineArgs(); | 			var args = Environment.GetCommandLineArgs(); | ||||||
| 			var configuration = BuildConfigurationRepository(); |  | ||||||
| 			var nativeMethods = new NativeMethods(); | 			var nativeMethods = new NativeMethods(); | ||||||
| 
 | 
 | ||||||
| 			logger = new Logger(); | 			logger = new Logger(); | ||||||
| 			appConfig = configuration.InitializeAppConfig(); |  | ||||||
| 			systemInfo = new SystemInfo(); | 			systemInfo = new SystemInfo(); | ||||||
| 
 | 
 | ||||||
|  | 			InitializeConfiguration(); | ||||||
| 			InitializeLogging(); | 			InitializeLogging(); | ||||||
| 			InitializeText(); | 			InitializeText(); | ||||||
| 
 | 
 | ||||||
|  | @ -60,7 +62,6 @@ namespace SafeExamBrowser.Runtime | ||||||
| 			var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods); | 			var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods); | ||||||
| 			var processFactory = new ProcessFactory(new ModuleLogger(logger, nameof(ProcessFactory))); | 			var processFactory = new ProcessFactory(new ModuleLogger(logger, nameof(ProcessFactory))); | ||||||
| 			var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), logger); | 			var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), logger); | ||||||
| 			var resourceLoader = new ResourceLoader(); |  | ||||||
| 			var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, 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 serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(ServiceProxy))); | ||||||
| 			var sessionContext = new SessionContext(); | 			var sessionContext = new SessionContext(); | ||||||
|  | @ -73,7 +74,7 @@ namespace SafeExamBrowser.Runtime | ||||||
| 			bootstrapOperations.Enqueue(new CommunicationHostOperation(runtimeHost, logger)); | 			bootstrapOperations.Enqueue(new CommunicationHostOperation(runtimeHost, logger)); | ||||||
| 
 | 
 | ||||||
| 			sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost, sessionContext)); | 			sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost, sessionContext)); | ||||||
| 			sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, logger, resourceLoader, sessionContext)); | 			sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, logger, sessionContext)); | ||||||
| 			sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, FIFTEEN_SECONDS)); | 			sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, FIFTEEN_SECONDS)); | ||||||
| 			sessionOperations.Enqueue(new KioskModeTerminationOperation(desktopFactory, explorerShell, logger, processFactory, sessionContext)); | 			sessionOperations.Enqueue(new KioskModeTerminationOperation(desktopFactory, explorerShell, logger, processFactory, sessionContext)); | ||||||
| 			sessionOperations.Enqueue(new ServiceOperation(logger, serviceProxy, sessionContext)); | 			sessionOperations.Enqueue(new ServiceOperation(logger, serviceProxy, sessionContext)); | ||||||
|  | @ -105,15 +106,22 @@ namespace SafeExamBrowser.Runtime | ||||||
| 			logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); | 			logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private IConfigurationRepository BuildConfigurationRepository() | 		private void InitializeConfiguration() | ||||||
| 		{ | 		{ | ||||||
| 			var executable = Assembly.GetExecutingAssembly(); | 			var executable = Assembly.GetExecutingAssembly(); | ||||||
| 			var programCopyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright; | 			var programCopyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright; | ||||||
| 			var programTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title; | 			var programTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title; | ||||||
| 			var programVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion; | 			var programVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion; | ||||||
| 			var repository = new ConfigurationRepository(executable.Location, programCopyright, programTitle, programVersion); | 			var moduleLogger = new ModuleLogger(logger, nameof(ConfigurationRepository)); | ||||||
| 
 | 
 | ||||||
| 			return repository; | 			configuration = new ConfigurationRepository(moduleLogger, executable.Location, programCopyright, programTitle, programVersion); | ||||||
|  | 			appConfig = configuration.InitializeAppConfig(); | ||||||
|  | 
 | ||||||
|  | 			configuration.Register(new DefaultFormat(new ModuleLogger(logger, nameof(DefaultFormat)))); | ||||||
|  | 			configuration.Register(new HtmlFormat(new ModuleLogger(logger, nameof(HtmlFormat)))); | ||||||
|  | 			configuration.Register(new XmlFormat(new ModuleLogger(logger, nameof(XmlFormat)))); | ||||||
|  | 			configuration.Register(new FileResourceLoader(new ModuleLogger(logger, nameof(FileResourceLoader)))); | ||||||
|  | 			configuration.Register(new NetworkResourceLoader(appConfig, new ModuleLogger(logger, nameof(NetworkResourceLoader)))); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private void InitializeLogging() | 		private void InitializeLogging() | ||||||
|  |  | ||||||
|  | @ -24,7 +24,6 @@ namespace SafeExamBrowser.Runtime.Operations | ||||||
| 		private string[] commandLineArgs; | 		private string[] commandLineArgs; | ||||||
| 		private IConfigurationRepository configuration; | 		private IConfigurationRepository configuration; | ||||||
| 		private ILogger logger; | 		private ILogger logger; | ||||||
| 		private IResourceLoader resourceLoader; |  | ||||||
| 
 | 
 | ||||||
| 		public override event ActionRequiredEventHandler ActionRequired; | 		public override event ActionRequiredEventHandler ActionRequired; | ||||||
| 		public override event StatusChangedEventHandler StatusChanged; | 		public override event StatusChangedEventHandler StatusChanged; | ||||||
|  | @ -33,13 +32,11 @@ namespace SafeExamBrowser.Runtime.Operations | ||||||
| 			string[] commandLineArgs, | 			string[] commandLineArgs, | ||||||
| 			IConfigurationRepository configuration, | 			IConfigurationRepository configuration, | ||||||
| 			ILogger logger, | 			ILogger logger, | ||||||
| 			IResourceLoader resourceLoader, |  | ||||||
| 			SessionContext sessionContext) : base(sessionContext) | 			SessionContext sessionContext) : base(sessionContext) | ||||||
| 		{ | 		{ | ||||||
| 			this.commandLineArgs = commandLineArgs; | 			this.commandLineArgs = commandLineArgs; | ||||||
| 			this.logger = logger; | 			this.logger = logger; | ||||||
| 			this.configuration = configuration; | 			this.configuration = configuration; | ||||||
| 			this.resourceLoader = resourceLoader; |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public override OperationResult Perform() | 		public override OperationResult Perform() | ||||||
|  | @ -51,7 +48,7 @@ namespace SafeExamBrowser.Runtime.Operations | ||||||
| 
 | 
 | ||||||
| 			if (isValidUri) | 			if (isValidUri) | ||||||
| 			{ | 			{ | ||||||
| 				logger.Info($"Attempting to load settings from '{uri.AbsolutePath}'..."); | 				logger.Info($"Attempting to load settings from '{uri}'..."); | ||||||
| 
 | 
 | ||||||
| 				var result = LoadSettings(uri); | 				var result = LoadSettings(uri); | ||||||
| 
 | 
 | ||||||
|  | @ -62,7 +59,7 @@ namespace SafeExamBrowser.Runtime.Operations | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			logger.Info("No valid settings resource specified nor found in PROGRAMDATA or APPDATA - loading default settings..."); | 			logger.Info("No valid settings resource specified nor found in PROGRAMDATA or APPDATA - loading default settings..."); | ||||||
| 			configuration.LoadDefaultSettings(); | 			Context.Next.Settings = configuration.LoadDefaultSettings(); | ||||||
| 
 | 
 | ||||||
| 			return OperationResult.Success; | 			return OperationResult.Success; | ||||||
| 		} | 		} | ||||||
|  | @ -76,7 +73,7 @@ namespace SafeExamBrowser.Runtime.Operations | ||||||
| 
 | 
 | ||||||
| 			if (isValidUri) | 			if (isValidUri) | ||||||
| 			{ | 			{ | ||||||
| 				logger.Info($"Attempting to load settings from '{uri.AbsolutePath}'..."); | 				logger.Info($"Attempting to load settings from '{uri}'..."); | ||||||
| 
 | 
 | ||||||
| 				var result = LoadSettings(uri); | 				var result = LoadSettings(uri); | ||||||
| 
 | 
 | ||||||
|  | @ -126,11 +123,6 @@ namespace SafeExamBrowser.Runtime.Operations | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (status == LoadStatus.InvalidData) |  | ||||||
| 			{ |  | ||||||
| 				HandleInvalidData(ref status, uri); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (status == LoadStatus.Success) | 			if (status == LoadStatus.Success) | ||||||
| 			{ | 			{ | ||||||
| 				Context.Next.Settings = settings; | 				Context.Next.Settings = settings; | ||||||
|  | @ -138,6 +130,18 @@ namespace SafeExamBrowser.Runtime.Operations | ||||||
| 				return OperationResult.Success; | 				return OperationResult.Success; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			switch (status) | ||||||
|  | 			{ | ||||||
|  | 				case LoadStatus.InvalidData: | ||||||
|  | 					ActionRequired?.Invoke(new InvalidDataMessageArgs(uri.ToString())); | ||||||
|  | 					break; | ||||||
|  | 				case LoadStatus.NotSupported: | ||||||
|  | 					ActionRequired?.Invoke(new NotSupportedMessageArgs(uri.ToString())); | ||||||
|  | 					break; | ||||||
|  | 				case LoadStatus.UnexpectedError: | ||||||
|  | 					ActionRequired?.Invoke(new UnexpectedErrorMessageArgs(uri.ToString())); | ||||||
|  | 					break; | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			return OperationResult.Failed; | 			return OperationResult.Failed; | ||||||
| 		} | 		} | ||||||
|  | @ -152,22 +156,6 @@ namespace SafeExamBrowser.Runtime.Operations | ||||||
| 			return args; | 			return args; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private void HandleInvalidData(ref LoadStatus status, Uri uri) |  | ||||||
| 		{ |  | ||||||
| 			if (resourceLoader.IsHtmlResource(uri)) |  | ||||||
| 			{ |  | ||||||
| 				configuration.LoadDefaultSettings(); |  | ||||||
| 				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; |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ |  | ||||||
| 				logger.Error($"The specified settings resource '{uri.AbsoluteUri}' is invalid!"); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		private bool TryInitializeSettingsUri(out Uri uri) | 		private bool TryInitializeSettingsUri(out Uri uri) | ||||||
| 		{ | 		{ | ||||||
| 			var path = string.Empty; | 			var path = string.Empty; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | /* | ||||||
|  |  * 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.I18n; | ||||||
|  | using SafeExamBrowser.Contracts.UserInterface.MessageBox; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Runtime.Operations.Events | ||||||
|  | { | ||||||
|  | 	internal class InvalidDataMessageArgs : MessageEventArgs | ||||||
|  | 	{ | ||||||
|  | 		internal InvalidDataMessageArgs(string uri) | ||||||
|  | 		{ | ||||||
|  | 			Icon = MessageBoxIcon.Error; | ||||||
|  | 			Message = TextKey.MessageBox_InvalidConfigurationData; | ||||||
|  | 			MessagePlaceholders["%%URI%%"] = uri; | ||||||
|  | 			Title = TextKey.MessageBox_InvalidConfigurationDataTitle; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | /* | ||||||
|  |  * 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.Collections.Generic; | ||||||
|  | using SafeExamBrowser.Contracts.Core.OperationModel.Events; | ||||||
|  | using SafeExamBrowser.Contracts.I18n; | ||||||
|  | using SafeExamBrowser.Contracts.UserInterface.MessageBox; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Runtime.Operations.Events | ||||||
|  | { | ||||||
|  | 	internal class MessageEventArgs : ActionRequiredEventArgs | ||||||
|  | 	{ | ||||||
|  | 		internal MessageBoxIcon Icon { get; set; } | ||||||
|  | 		internal TextKey Message { get; set; } | ||||||
|  | 		internal TextKey Title { get; set; } | ||||||
|  | 		internal Dictionary<string, string> MessagePlaceholders { get; private set; } | ||||||
|  | 		internal Dictionary<string, string> TitlePlaceholders { get; private set; } | ||||||
|  | 
 | ||||||
|  | 		public MessageEventArgs() | ||||||
|  | 		{ | ||||||
|  | 			MessagePlaceholders = new Dictionary<string, string>(); | ||||||
|  | 			TitlePlaceholders = new Dictionary<string, string>(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | /* | ||||||
|  |  * 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.I18n; | ||||||
|  | using SafeExamBrowser.Contracts.UserInterface.MessageBox; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Runtime.Operations.Events | ||||||
|  | { | ||||||
|  | 	internal class NotSupportedMessageArgs : MessageEventArgs | ||||||
|  | 	{ | ||||||
|  | 		internal NotSupportedMessageArgs(string uri) | ||||||
|  | 		{ | ||||||
|  | 			Icon = MessageBoxIcon.Error; | ||||||
|  | 			Message = TextKey.MessageBox_NotSupportedConfigurationResource; | ||||||
|  | 			MessagePlaceholders["%%URI%%"] = uri; | ||||||
|  | 			Title = TextKey.MessageBox_NotSupportedConfigurationResourceTitle; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | /* | ||||||
|  |  * 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.I18n; | ||||||
|  | using SafeExamBrowser.Contracts.UserInterface.MessageBox; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Runtime.Operations.Events | ||||||
|  | { | ||||||
|  | 	internal class UnexpectedErrorMessageArgs : MessageEventArgs | ||||||
|  | 	{ | ||||||
|  | 		internal UnexpectedErrorMessageArgs(string uri) | ||||||
|  | 		{ | ||||||
|  | 			Icon = MessageBoxIcon.Error; | ||||||
|  | 			Message = TextKey.MessageBox_UnexpectedConfigurationError; | ||||||
|  | 			MessagePlaceholders["%%URI%%"] = uri; | ||||||
|  | 			Title = TextKey.MessageBox_UnexpectedConfigurationErrorTitle; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -35,10 +35,6 @@ namespace SafeExamBrowser.Runtime.Operations | ||||||
| 			set { Context.OriginalDesktop = value; } | 			set { Context.OriginalDesktop = value; } | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/// <summary> |  | ||||||
| 		/// TODO: This mechanism exposes the internal state of the operation! Find better solution which will keep the |  | ||||||
| 		/// state internal but still allow unit testing of both kiosk mode operations independently! |  | ||||||
| 		/// </summary> |  | ||||||
| 		protected KioskMode? ActiveMode | 		protected KioskMode? ActiveMode | ||||||
| 		{ | 		{ | ||||||
| 			get { return Context.ActiveMode; } | 			get { return Context.ActiveMode; } | ||||||
|  |  | ||||||
|  | @ -363,6 +363,9 @@ namespace SafeExamBrowser.Runtime | ||||||
| 				case ConfigurationCompletedEventArgs a: | 				case ConfigurationCompletedEventArgs a: | ||||||
| 					AskIfConfigurationSufficient(a); | 					AskIfConfigurationSufficient(a); | ||||||
| 					break; | 					break; | ||||||
|  | 				case MessageEventArgs m: | ||||||
|  | 					ShowMessageBox(m); | ||||||
|  | 					break; | ||||||
| 				case PasswordRequiredEventArgs p: | 				case PasswordRequiredEventArgs p: | ||||||
| 					AskForPassword(p); | 					AskForPassword(p); | ||||||
| 					break; | 					break; | ||||||
|  | @ -393,6 +396,24 @@ namespace SafeExamBrowser.Runtime | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		private void ShowMessageBox(MessageEventArgs args) | ||||||
|  | 		{ | ||||||
|  | 			var message = text.Get(args.Message); | ||||||
|  | 			var title = text.Get(args.Title); | ||||||
|  | 
 | ||||||
|  | 			foreach (var placeholder in args.MessagePlaceholders) | ||||||
|  | 			{ | ||||||
|  | 				message = message.Replace(placeholder.Key, placeholder.Value); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			foreach (var placeholder in args.TitlePlaceholders) | ||||||
|  | 			{ | ||||||
|  | 				title = title.Replace(placeholder.Key, placeholder.Value); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			messageBox.Show(message, title, MessageBoxAction.Confirm, args.Icon, runtimeWindow); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		private void TryGetPasswordViaDialog(PasswordRequiredEventArgs args) | 		private void TryGetPasswordViaDialog(PasswordRequiredEventArgs args) | ||||||
| 		{ | 		{ | ||||||
| 			var isAdmin = args.Purpose == PasswordRequestPurpose.Administrator; | 			var isAdmin = args.Purpose == PasswordRequestPurpose.Administrator; | ||||||
|  |  | ||||||
|  | @ -90,7 +90,11 @@ | ||||||
|     <Compile Include="Operations\ClientTerminationOperation.cs" /> |     <Compile Include="Operations\ClientTerminationOperation.cs" /> | ||||||
|     <Compile Include="Operations\ConfigurationOperation.cs" /> |     <Compile Include="Operations\ConfigurationOperation.cs" /> | ||||||
|     <Compile Include="Operations\Events\ConfigurationCompletedEventArgs.cs" /> |     <Compile Include="Operations\Events\ConfigurationCompletedEventArgs.cs" /> | ||||||
|  |     <Compile Include="Operations\Events\InvalidDataMessageArgs.cs" /> | ||||||
|  |     <Compile Include="Operations\Events\MessageEventArgs.cs" /> | ||||||
|  |     <Compile Include="Operations\Events\NotSupportedMessageArgs.cs" /> | ||||||
|     <Compile Include="Operations\Events\PasswordRequiredEventArgs.cs" /> |     <Compile Include="Operations\Events\PasswordRequiredEventArgs.cs" /> | ||||||
|  |     <Compile Include="Operations\Events\UnexpectedErrorMessageArgs.cs" /> | ||||||
|     <Compile Include="Operations\KioskModeOperation.cs" /> |     <Compile Include="Operations\KioskModeOperation.cs" /> | ||||||
|     <Compile Include="Operations\KioskModeTerminationOperation.cs" /> |     <Compile Include="Operations\KioskModeTerminationOperation.cs" /> | ||||||
|     <Compile Include="Operations\ServiceOperation.cs" /> |     <Compile Include="Operations\ServiceOperation.cs" /> | ||||||
|  |  | ||||||
|  | @ -64,6 +64,7 @@ namespace SafeExamBrowser.WindowsApi | ||||||
| 				logger.Info($"Restored window '{window.Title}' with handle = {window.Handle}."); | 				logger.Info($"Restored window '{window.Title}' with handle = {window.Handle}."); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			minimizedWindows.Clear(); | ||||||
| 			logger.Info("Minimized windows successfully restored."); | 			logger.Info("Minimized windows successfully restored."); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 dbuechel
						dbuechel