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) | ||||
| 		{ | ||||
| 			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) | ||||
|  |  | |||
|  | @ -31,11 +31,11 @@ namespace SafeExamBrowser.Browser.Handlers | |||
| 
 | ||||
| 			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) | ||||
| 			{ | ||||
| 				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); | ||||
|  |  | |||
|  | @ -206,7 +206,7 @@ namespace SafeExamBrowser.Client | |||
| 		{ | ||||
| 			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 title = TextKey.MessageBox_ReconfigurationQuestionTitle; | ||||
|  |  | |||
|  | @ -9,7 +9,9 @@ | |||
| using System; | ||||
| using System.Reflection; | ||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
| using Moq; | ||||
| using SafeExamBrowser.Contracts.Configuration; | ||||
| using SafeExamBrowser.Contracts.Logging; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Configuration.UnitTests | ||||
| { | ||||
|  | @ -22,8 +24,9 @@ namespace SafeExamBrowser.Configuration.UnitTests | |||
| 		public void Initialize() | ||||
| 		{ | ||||
| 			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] | ||||
|  |  | |||
|  | @ -7,7 +7,11 @@ | |||
|  */ | ||||
| 
 | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.IO.Compression; | ||||
| using System.Linq; | ||||
| using SafeExamBrowser.Configuration.DataFormats; | ||||
| using SafeExamBrowser.Contracts.Configuration; | ||||
| using SafeExamBrowser.Contracts.Configuration.Settings; | ||||
| using SafeExamBrowser.Contracts.Logging; | ||||
|  | @ -24,9 +28,16 @@ namespace SafeExamBrowser.Configuration | |||
| 		private readonly string programVersion; | ||||
| 
 | ||||
| 		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.programCopyright = programCopyright ?? string.Empty; | ||||
| 			this.programTitle = programTitle ?? string.Empty; | ||||
|  | @ -73,38 +84,28 @@ namespace SafeExamBrowser.Configuration | |||
| 
 | ||||
| 			UpdateAppConfig(); | ||||
| 
 | ||||
| 			configuration.AppConfig = CloneAppConfig(); | ||||
| 			configuration.AppConfig = appConfig.Clone(); | ||||
| 			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.KioskMode = KioskMode.None; | ||||
| 			settings.ServicePolicy = ServicePolicy.Optional; | ||||
| 
 | ||||
| 			settings.Browser.StartUrl = "https://www.safeexambrowser.org/testing"; | ||||
| 			settings.Browser.AllowAddressBar = true; | ||||
| 			settings.Browser.AllowBackwardNavigation = true; | ||||
| 			settings.Browser.AllowConfigurationDownloads = true; | ||||
| 			settings.Browser.AllowDeveloperConsole = true; | ||||
| 			settings.Browser.AllowDownloads = true; | ||||
| 			settings.Browser.AllowForwardNavigation = true; | ||||
| 			settings.Browser.AllowReloading = true; | ||||
| 			settings.Browser.AllowDownloads = true; | ||||
| 
 | ||||
| 			settings.Taskbar.AllowApplicationLog = true; | ||||
| 			settings.Taskbar.AllowKeyboardLayout = true; | ||||
|  | @ -113,33 +114,90 @@ namespace SafeExamBrowser.Configuration | |||
| 			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, | ||||
| 				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 | ||||
| 			}; | ||||
| 				var resourceLoader = resourceLoaders.FirstOrDefault(l => l.CanLoad(resource)); | ||||
| 
 | ||||
| 				if (resourceLoader != null) | ||||
| 				{ | ||||
| 					var data = resourceLoader.Load(resource); | ||||
| 					var dataFormat = dataFormats.FirstOrDefault(f => f.CanParse(data)); | ||||
| 
 | ||||
| 					logger.Info($"Successfully loaded {data.Length / 1000.0} KB data from '{resource}' using {resourceLoader.GetType().Name}."); | ||||
| 
 | ||||
| 					if (dataFormat is HtmlFormat) | ||||
| 					{ | ||||
| 						return HandleHtml(resource, out settings); | ||||
| 					} | ||||
| 
 | ||||
| 					if (dataFormat != null) | ||||
| 					{ | ||||
| 						return dataFormat.TryParse(data, out settings, adminPassword, settingsPassword); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				logger.Warn($"No {(resourceLoader == null ? "resource loader" : "data format")} found for '{resource}'!"); | ||||
| 
 | ||||
| 				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() | ||||
|  |  | |||
							
								
								
									
										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> | ||||
|   <ItemGroup> | ||||
|     <Reference Include="System" /> | ||||
|     <Reference Include="System.Net.Http" /> | ||||
|     <Reference Include="System.Windows.Forms" /> | ||||
|     <Reference Include="Microsoft.CSharp" /> | ||||
|   </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="ConfigurationRepository.cs" /> | ||||
|     <Compile Include="ResourceLoaders\FileResourceLoader.cs" /> | ||||
|     <Compile Include="ResourceLoaders\NetworkResourceLoader.cs" /> | ||||
|     <Compile Include="SessionConfiguration.cs" /> | ||||
|     <Compile Include="SystemInfo.cs" /> | ||||
|   </ItemGroup> | ||||
|  |  | |||
|  | @ -126,5 +126,13 @@ namespace SafeExamBrowser.Contracts.Configuration | |||
| 		/// The communication address of the service component. | ||||
| 		/// </summary> | ||||
| 		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> | ||||
| 		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> | ||||
| 		/// Loads the default settings. | ||||
| 		/// </summary> | ||||
| 		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 | ||||
| { | ||||
| 	/// <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> | ||||
| 	public interface IResourceLoader | ||||
| 	{ | ||||
| 		/// <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> | ||||
| 		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 | ||||
| { | ||||
| 	/// <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> | ||||
| 	public enum LoadStatus | ||||
| 	{ | ||||
|  | @ -23,14 +23,24 @@ namespace SafeExamBrowser.Contracts.Configuration | |||
| 		/// </summary> | ||||
| 		InvalidData, | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Indicates that a resource is not supported. | ||||
| 		/// </summary> | ||||
| 		NotSupported, | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Indicates that a settings password is needed in order to load the settings. | ||||
| 		/// </summary> | ||||
| 		SettingsPasswordNeeded, | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The <see cref="Settings.Settings"/> were loaded successfully. | ||||
| 		/// The settings were loaded successfully. | ||||
| 		/// </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> | ||||
| 		public bool AllowBackwardNavigation { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Determines whether the user should be allowed to download configuration files. | ||||
| 		/// </summary> | ||||
| 		public bool AllowConfigurationDownloads { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Determines whether the user should be allowed to open the developer console of a browser window. | ||||
| 		/// </summary> | ||||
| 		public bool AllowDeveloperConsole { get; set; } | ||||
| 
 | ||||
| 		/// <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> | ||||
| 		public bool AllowDownloads { get; set; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,6 +22,10 @@ namespace SafeExamBrowser.Contracts.I18n | |||
| 		MessageBox_ClientConfigurationQuestionTitle, | ||||
| 		MessageBox_ConfigurationDownloadError, | ||||
| 		MessageBox_ConfigurationDownloadErrorTitle, | ||||
| 		MessageBox_InvalidConfigurationData, | ||||
| 		MessageBox_InvalidConfigurationDataTitle, | ||||
| 		MessageBox_NotSupportedConfigurationResource, | ||||
| 		MessageBox_NotSupportedConfigurationResourceTitle, | ||||
| 		MessageBox_Quit, | ||||
| 		MessageBox_QuitTitle, | ||||
| 		MessageBox_QuitError, | ||||
|  | @ -42,6 +46,8 @@ namespace SafeExamBrowser.Contracts.I18n | |||
| 		MessageBox_SingleInstanceTitle, | ||||
| 		MessageBox_StartupError, | ||||
| 		MessageBox_StartupErrorTitle, | ||||
| 		MessageBox_UnexpectedConfigurationError, | ||||
| 		MessageBox_UnexpectedConfigurationErrorTitle, | ||||
| 		Notification_AboutTooltip, | ||||
| 		Notification_LogTooltip, | ||||
| 		OperationStatus_CloseRuntimeConnection, | ||||
|  |  | |||
|  | @ -53,6 +53,7 @@ | |||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="Communication\Events\ClientConfigurationEventArgs.cs" /> | ||||
|     <Compile Include="Configuration\IDataFormat.cs" /> | ||||
|     <Compile Include="Core\Events\InstanceTerminatedEventHandler.cs" /> | ||||
|     <Compile Include="Core\Events\NameChangedEventHandler.cs" /> | ||||
|     <Compile Include="Core\IApplicationController.cs" /> | ||||
|  |  | |||
|  | @ -24,6 +24,18 @@ | |||
|   <Entry key="MessageBox_ConfigurationDownloadErrorTitle"> | ||||
|     Download Error | ||||
|   </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"> | ||||
|     Would you really like to quit the application? | ||||
|   </Entry> | ||||
|  | @ -78,6 +90,12 @@ | |||
|   <Entry key="MessageBox_StartupErrorTitle"> | ||||
|     Startup Error | ||||
|   </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"> | ||||
|     About Safe Exam Browser | ||||
|   </Entry> | ||||
|  |  | |||
|  | @ -26,7 +26,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | |||
| 		private AppConfig appConfig; | ||||
| 		private Mock<ILogger> logger; | ||||
| 		private Mock<IConfigurationRepository> repository; | ||||
| 		private Mock<IResourceLoader> resourceLoader; | ||||
| 		private Mock<ISessionConfiguration> session; | ||||
| 		private SessionContext sessionContext; | ||||
| 		private Settings settings; | ||||
|  | @ -39,7 +38,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | |||
| 			appConfig = new AppConfig(); | ||||
| 			logger = new Mock<ILogger>(); | ||||
| 			repository = new Mock<IConfigurationRepository>(); | ||||
| 			resourceLoader = new Mock<IResourceLoader>(); | ||||
| 			session = new Mock<ISessionConfiguration>(); | ||||
| 			sessionContext = new SessionContext(); | ||||
| 			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); | ||||
| 
 | ||||
| 			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(); | ||||
| 
 | ||||
| 			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); | ||||
| 
 | ||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | ||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||
| 			sut.Perform(); | ||||
| 
 | ||||
| 			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); | ||||
| 
 | ||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | ||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||
| 			sut.Perform(); | ||||
| 
 | ||||
| 			var resource = new Uri(Path.Combine(location, "SettingsDummy.txt")); | ||||
|  | @ -110,10 +108,19 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | |||
| 		[TestMethod] | ||||
| 		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(); | ||||
| 
 | ||||
| 			repository.Verify(r => r.LoadDefaultSettings(), Times.Once); | ||||
| 			session.VerifySet(s => s.Settings = defaultSettings); | ||||
| 
 | ||||
| 			Assert.AreSame(defaultSettings, actualSettings); | ||||
| 		} | ||||
| 
 | ||||
| 		[TestMethod] | ||||
|  | @ -122,7 +129,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.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); | ||||
| 
 | ||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | ||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||
| 			sut.ActionRequired += args => | ||||
| 			{ | ||||
| 				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); | ||||
| 
 | ||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext); | ||||
| 			sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext); | ||||
| 			sut.ActionRequired += args => | ||||
| 			{ | ||||
| 				if (args is ConfigurationCompletedEventArgs c) | ||||
|  | @ -161,7 +168,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | |||
| 			settings.ConfigurationMode = ConfigurationMode.Exam; | ||||
| 			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 => | ||||
| 			{ | ||||
| 				if (args is ConfigurationCompletedEventArgs c) | ||||
|  | @ -176,15 +183,25 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | |||
| 		[TestMethod] | ||||
| 		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 = 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(); | ||||
| 
 | ||||
| 			repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2)); | ||||
| 
 | ||||
| 			Assert.AreSame(defaultSettings, actualSettings); | ||||
| 		} | ||||
| 
 | ||||
| 		[TestMethod] | ||||
|  | @ -192,7 +209,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | |||
| 		{ | ||||
| 			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(); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -203,7 +220,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | |||
| 
 | ||||
| 			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 => | ||||
| 			{ | ||||
| 				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); | ||||
| 
 | ||||
| 			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 => | ||||
| 			{ | ||||
| 				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, 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 => | ||||
| 			{ | ||||
| 				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, 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 => | ||||
| 			{ | ||||
| 				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); | ||||
| 
 | ||||
| 			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 => | ||||
| 			{ | ||||
| 				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); | ||||
| 
 | ||||
| 			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 => | ||||
| 			{ | ||||
| 				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, 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 => | ||||
| 			{ | ||||
| 				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); | ||||
| 		} | ||||
| 
 | ||||
| 		[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] | ||||
| 		public void MustReconfigureSuccessfullyWithCorrectUri() | ||||
| 		{ | ||||
|  | @ -398,7 +384,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | |||
| 			sessionContext.ReconfigurationFilePath = resource.AbsolutePath; | ||||
| 			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(); | ||||
| 
 | ||||
|  | @ -415,7 +401,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations | |||
| 			sessionContext.ReconfigurationFilePath = null; | ||||
| 			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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,6 +6,10 @@ | |||
|         <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" /> | ||||
|       </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> | ||||
|   </runtime> | ||||
| </configuration> | ||||
|  | @ -13,6 +13,8 @@ using System.Reflection; | |||
| using SafeExamBrowser.Communication.Hosts; | ||||
| using SafeExamBrowser.Communication.Proxies; | ||||
| using SafeExamBrowser.Configuration; | ||||
| using SafeExamBrowser.Configuration.DataFormats; | ||||
| using SafeExamBrowser.Configuration.ResourceLoaders; | ||||
| using SafeExamBrowser.Contracts.Configuration; | ||||
| using SafeExamBrowser.Contracts.Core; | ||||
| using SafeExamBrowser.Contracts.Core.OperationModel; | ||||
|  | @ -32,6 +34,7 @@ namespace SafeExamBrowser.Runtime | |||
| 	internal class CompositionRoot | ||||
| 	{ | ||||
| 		private AppConfig appConfig; | ||||
| 		private IConfigurationRepository configuration; | ||||
| 		private ILogger logger; | ||||
| 		private ISystemInfo systemInfo; | ||||
| 		private IText text; | ||||
|  | @ -45,13 +48,12 @@ namespace SafeExamBrowser.Runtime | |||
| 			const int FIFTEEN_SECONDS = 15000; | ||||
| 
 | ||||
| 			var args = Environment.GetCommandLineArgs(); | ||||
| 			var configuration = BuildConfigurationRepository(); | ||||
| 			var nativeMethods = new NativeMethods(); | ||||
| 
 | ||||
| 			logger = new Logger(); | ||||
| 			appConfig = configuration.InitializeAppConfig(); | ||||
| 			systemInfo = new SystemInfo(); | ||||
| 
 | ||||
| 			InitializeConfiguration(); | ||||
| 			InitializeLogging(); | ||||
| 			InitializeText(); | ||||
| 
 | ||||
|  | @ -60,7 +62,6 @@ namespace SafeExamBrowser.Runtime | |||
| 			var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods); | ||||
| 			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, 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(); | ||||
|  | @ -73,7 +74,7 @@ namespace SafeExamBrowser.Runtime | |||
| 			bootstrapOperations.Enqueue(new CommunicationHostOperation(runtimeHost, logger)); | ||||
| 
 | ||||
| 			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 KioskModeTerminationOperation(desktopFactory, explorerShell, logger, processFactory, 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")}"); | ||||
| 		} | ||||
| 
 | ||||
| 		private IConfigurationRepository BuildConfigurationRepository() | ||||
| 		private void InitializeConfiguration() | ||||
| 		{ | ||||
| 			var executable = Assembly.GetExecutingAssembly(); | ||||
| 			var programCopyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright; | ||||
| 			var programTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title; | ||||
| 			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() | ||||
|  |  | |||
|  | @ -24,7 +24,6 @@ namespace SafeExamBrowser.Runtime.Operations | |||
| 		private string[] commandLineArgs; | ||||
| 		private IConfigurationRepository configuration; | ||||
| 		private ILogger logger; | ||||
| 		private IResourceLoader resourceLoader; | ||||
| 
 | ||||
| 		public override event ActionRequiredEventHandler ActionRequired; | ||||
| 		public override event StatusChangedEventHandler StatusChanged; | ||||
|  | @ -33,13 +32,11 @@ namespace SafeExamBrowser.Runtime.Operations | |||
| 			string[] commandLineArgs, | ||||
| 			IConfigurationRepository configuration, | ||||
| 			ILogger logger, | ||||
| 			IResourceLoader resourceLoader, | ||||
| 			SessionContext sessionContext) : base(sessionContext) | ||||
| 		{ | ||||
| 			this.commandLineArgs = commandLineArgs; | ||||
| 			this.logger = logger; | ||||
| 			this.configuration = configuration; | ||||
| 			this.resourceLoader = resourceLoader; | ||||
| 		} | ||||
| 
 | ||||
| 		public override OperationResult Perform() | ||||
|  | @ -51,7 +48,7 @@ namespace SafeExamBrowser.Runtime.Operations | |||
| 
 | ||||
| 			if (isValidUri) | ||||
| 			{ | ||||
| 				logger.Info($"Attempting to load settings from '{uri.AbsolutePath}'..."); | ||||
| 				logger.Info($"Attempting to load settings from '{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..."); | ||||
| 			configuration.LoadDefaultSettings(); | ||||
| 			Context.Next.Settings = configuration.LoadDefaultSettings(); | ||||
| 
 | ||||
| 			return OperationResult.Success; | ||||
| 		} | ||||
|  | @ -76,7 +73,7 @@ namespace SafeExamBrowser.Runtime.Operations | |||
| 
 | ||||
| 			if (isValidUri) | ||||
| 			{ | ||||
| 				logger.Info($"Attempting to load settings from '{uri.AbsolutePath}'..."); | ||||
| 				logger.Info($"Attempting to load settings from '{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) | ||||
| 			{ | ||||
| 				Context.Next.Settings = settings; | ||||
|  | @ -138,6 +130,18 @@ namespace SafeExamBrowser.Runtime.Operations | |||
| 				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; | ||||
| 		} | ||||
|  | @ -152,22 +156,6 @@ namespace SafeExamBrowser.Runtime.Operations | |||
| 			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) | ||||
| 		{ | ||||
| 			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; } | ||||
| 		} | ||||
| 
 | ||||
| 		/// <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 | ||||
| 		{ | ||||
| 			get { return Context.ActiveMode; } | ||||
|  |  | |||
|  | @ -363,6 +363,9 @@ namespace SafeExamBrowser.Runtime | |||
| 				case ConfigurationCompletedEventArgs a: | ||||
| 					AskIfConfigurationSufficient(a); | ||||
| 					break; | ||||
| 				case MessageEventArgs m: | ||||
| 					ShowMessageBox(m); | ||||
| 					break; | ||||
| 				case PasswordRequiredEventArgs p: | ||||
| 					AskForPassword(p); | ||||
| 					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) | ||||
| 		{ | ||||
| 			var isAdmin = args.Purpose == PasswordRequestPurpose.Administrator; | ||||
|  |  | |||
|  | @ -90,7 +90,11 @@ | |||
|     <Compile Include="Operations\ClientTerminationOperation.cs" /> | ||||
|     <Compile Include="Operations\ConfigurationOperation.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\UnexpectedErrorMessageArgs.cs" /> | ||||
|     <Compile Include="Operations\KioskModeOperation.cs" /> | ||||
|     <Compile Include="Operations\KioskModeTerminationOperation.cs" /> | ||||
|     <Compile Include="Operations\ServiceOperation.cs" /> | ||||
|  |  | |||
|  | @ -64,6 +64,7 @@ namespace SafeExamBrowser.WindowsApi | |||
| 				logger.Info($"Restored window '{window.Title}' with handle = {window.Handle}."); | ||||
| 			} | ||||
| 
 | ||||
| 			minimizedWindows.Clear(); | ||||
| 			logger.Info("Minimized windows successfully restored."); | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 dbuechel
						dbuechel