diff --git a/SafeExamBrowser.ResetUtility/App.config b/SafeExamBrowser.ResetUtility/App.config new file mode 100644 index 00000000..56efbc7b --- /dev/null +++ b/SafeExamBrowser.ResetUtility/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SafeExamBrowser.ResetUtility/CompositionRoot.cs b/SafeExamBrowser.ResetUtility/CompositionRoot.cs new file mode 100644 index 00000000..9dd21e7d --- /dev/null +++ b/SafeExamBrowser.ResetUtility/CompositionRoot.cs @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 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.Logging; +using SafeExamBrowser.Logging; +using SafeExamBrowser.ResetUtility.Procedure; + +namespace SafeExamBrowser.ResetUtility +{ + internal class CompositionRoot + { + private ILogger logger; + + internal ProcedureStep InitialStep { get; private set; } + internal NativeMethods NativeMethods { get; private set; } + + internal void BuildObjectGraph() + { + InitializeLogging(); + + var context = new Context + { + Logger = logger, + }; + + InitialStep = new Initialization(context); + NativeMethods = new NativeMethods(); + } + + internal void LogStartupInformation() + { + logger.Log($"# Application started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); + logger.Log(string.Empty); + } + + internal void LogShutdownInformation() + { + logger.Log(string.Empty); + logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); + } + + private void InitializeLogging() + { + var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(SafeExamBrowser)); + var logFolder = Path.Combine(appDataFolder, "Logs"); + var logFilePrefix = DateTime.Now.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s"); + var logFilePath = Path.Combine(logFolder, $"{logFilePrefix}_{nameof(ResetUtility)}.log"); + var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), logFilePath); + + logger = new Logger(); + logger.LogLevel = LogLevel.Debug; + logger.Subscribe(logFileWriter); + logFileWriter.Initialize(); + } + } +} diff --git a/SafeExamBrowser.ResetUtility/Context.cs b/SafeExamBrowser.ResetUtility/Context.cs new file mode 100644 index 00000000..d472de97 --- /dev/null +++ b/SafeExamBrowser.ResetUtility/Context.cs @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2019 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.Logging; + +namespace SafeExamBrowser.ResetUtility +{ + internal class Context + { + internal ILogger Logger { get; set; } + } +} diff --git a/SafeExamBrowser.ResetUtility/NativeMethods.cs b/SafeExamBrowser.ResetUtility/NativeMethods.cs new file mode 100644 index 00000000..5d9e30b8 --- /dev/null +++ b/SafeExamBrowser.ResetUtility/NativeMethods.cs @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 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.Runtime.InteropServices; + +namespace SafeExamBrowser.ResetUtility +{ + internal class NativeMethods + { + private const int GWL_STYLE = -16; + private const int MF_BYCOMMAND = 0x0; + private const int SC_MAXIMIZE = 0xF030; + private const int SC_MINIMIZE = 0xF020; + private const int SC_SIZE = 0xF000; + private const int WS_MAXIMIZEBOX = 0x10000; + private const int WS_MINIMIZEBOX = 0x20000; + + internal void TryDisableSystemMenu() + { + try + { + // DeleteMenu(GetSystemMenu(GetConsoleWindow(), false), SC_MINIMIZE, MF_BYCOMMAND); + DeleteMenu(GetSystemMenu(GetConsoleWindow(), false), SC_MAXIMIZE, MF_BYCOMMAND); + DeleteMenu(GetSystemMenu(GetConsoleWindow(), false), SC_SIZE, MF_BYCOMMAND); + // SetWindowLong(GetConsoleWindow(), GWL_STYLE, GetWindowLong(GetConsoleWindow(), GWL_STYLE) & ~WS_MINIMIZEBOX); + SetWindowLong(GetConsoleWindow(), GWL_STYLE, GetWindowLong(GetConsoleWindow(), GWL_STYLE) & ~WS_MAXIMIZEBOX); + } + catch(Exception) { } + } + + [DllImport("user32.dll")] + private static extern int DeleteMenu(IntPtr hMenu, int nPosition, int wFlags); + + [DllImport("kernel32.dll")] + private static extern IntPtr GetConsoleWindow(); + + [DllImport("user32.dll")] + private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); + + [DllImport("user32.dll")] + private static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + } +} diff --git a/SafeExamBrowser.ResetUtility/Procedure/Initialization.cs b/SafeExamBrowser.ResetUtility/Procedure/Initialization.cs new file mode 100644 index 00000000..0cbf5a99 --- /dev/null +++ b/SafeExamBrowser.ResetUtility/Procedure/Initialization.cs @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019 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.Security.Principal; +using System.Threading; + +namespace SafeExamBrowser.ResetUtility.Procedure +{ + internal class Initialization : ProcedureStep + { + private static readonly Mutex mutex = new Mutex(true, "safe_exam_browser_reset_mutex"); + + public Initialization(Context context) : base(context) + { + } + + internal override ProcedureStepResult Execute() + { + Initialize(); + + if (IsSingleInstance() && HasAdminPrivileges() && SebNotRunning()) + { + return ProcedureStepResult.Continue; + } + else + { + return ProcedureStepResult.Terminate; + } + } + + internal override ProcedureStep GetNextStep() + { + return new MainMenu(Context); + } + + private bool IsSingleInstance() + { + var isSingle = mutex.WaitOne(TimeSpan.Zero, true); + + if (isSingle) + { + Logger.Info("There is currently no other instance running."); + } + else + { + Logger.Error("There is currently another instance running! Terminating..."); + ShowError("You can only run one instance of the Reset Utility at a time! Press any key to exit..."); + } + + return isSingle; + } + + private bool HasAdminPrivileges() + { + var identity = WindowsIdentity.GetCurrent(); + var principal = new WindowsPrincipal(identity); + var isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator); + + if (isAdmin) + { + Logger.Info($"User '{identity.Name}' is running the application with administrator privileges."); + } + else + { + Logger.Error($"User '{identity.Name}' is running the application without administrator privileges! Terminating..."); + ShowError("This application must be run with administrator privileges! Press any key to exit..."); + } + + return isAdmin; + } + + private bool SebNotRunning() + { + var isRunning = Mutex.TryOpenExisting("safe_exam_browser_runtime_mutex", out _); + + if (isRunning) + { + Logger.Error("SEB is currently running! Terminating..."); + ShowError("This application must not be run while SEB is running! Press any key to exit..."); + } + else + { + Logger.Info("SEB is currently not running."); + } + + return !isRunning; + } + } +} diff --git a/SafeExamBrowser.ResetUtility/Procedure/MainMenu.cs b/SafeExamBrowser.ResetUtility/Procedure/MainMenu.cs new file mode 100644 index 00000000..b742e16f --- /dev/null +++ b/SafeExamBrowser.ResetUtility/Procedure/MainMenu.cs @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 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.Collections.Generic; +using System.Linq; + +namespace SafeExamBrowser.ResetUtility.Procedure +{ + internal class MainMenu : ProcedureStep + { + private IList