diff --git a/Libraries/SimpleWifi.dll b/Libraries/SimpleWifi.dll deleted file mode 100644 index 3e28917a..00000000 Binary files a/Libraries/SimpleWifi.dll and /dev/null differ diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs index 9c53f207..265e936e 100644 --- a/SafeExamBrowser.Client/ClientController.cs +++ b/SafeExamBrowser.Client/ClientController.cs @@ -698,7 +698,9 @@ namespace SafeExamBrowser.Client private void NetworkAdapter_CredentialsRequired(CredentialsRequiredEventArgs args) { - var dialog = uiFactory.CreateNetworkDialog("TODO", "TODO"); + var message = text.Get(TextKey.CredentialsDialog_WirelessNetworkMessage).Replace("%%_NAME_%%", args.NetworkName); + var title = text.Get(TextKey.CredentialsDialog_WirelessNetworkTitle); + var dialog = uiFactory.CreateCredentialsDialog(CredentialsDialogPurpose.WirelessNetwork, message, title); var result = dialog.Show(); args.Password = result.Password; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs index d49961fe..61b84d98 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs @@ -26,26 +26,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData InitializeClipboardSettings(settings); InitializeProctoringSettings(settings); RemoveLegacyBrowsers(settings); - - settings.Applications.Blacklist.Clear(); - settings.Applications.Whitelist.Add(new WhitelistApplication { ExecutableName = "mspaint.exe", ShowInShell = true }); - settings.Browser.AdditionalWindow.AllowAddressBar = true; - settings.Browser.MainWindow.AllowAddressBar = true; - settings.Browser.MainWindow.AllowDeveloperConsole = true; - settings.Browser.MainWindow.AllowBackwardNavigation = true; - settings.Browser.MainWindow.AllowForwardNavigation = true; - settings.Browser.MainWindow.ShowHomeButton = true; - settings.Browser.MainWindow.UrlPolicy = Settings.Browser.UrlPolicy.BeforeTitle; - settings.Keyboard.AllowPrintScreen = true; - settings.LogLevel = Settings.Logging.LogLevel.Debug; - settings.Security.AllowApplicationLogAccess = true; - settings.Security.ClipboardPolicy = ClipboardPolicy.Allow; - settings.Security.KioskMode = KioskMode.CreateNewDesktop; - settings.Security.QuitPasswordHash = default; - settings.Service.IgnoreService = true; - settings.Service.Policy = Settings.Service.ServicePolicy.Optional; - settings.Security.VersionRestrictions.Clear(); - settings.Taskbar.ShowApplicationLog = true; } private void AllowBrowserToolbarForReloading(AppSettings settings) diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs index 6c17a2ea..72532e7c 100644 --- a/SafeExamBrowser.I18n.Contracts/TextKey.cs +++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs @@ -47,6 +47,11 @@ namespace SafeExamBrowser.I18n.Contracts BrowserWindow_ZoomMenuMinus, BrowserWindow_ZoomMenuPlus, Build, + CredentialsDialog_PasswordLabel, + CredentialsDialog_UsernameLabel, + CredentialsDialog_UsernameOptionalLabel, + CredentialsDialog_WirelessNetworkMessage, + CredentialsDialog_WirelessNetworkTitle, ExamSelectionDialog_Cancel, ExamSelectionDialog_Message, ExamSelectionDialog_Select, diff --git a/SafeExamBrowser.I18n/Data/de.xml b/SafeExamBrowser.I18n/Data/de.xml index 0fe5b60b..004c68fa 100644 --- a/SafeExamBrowser.I18n/Data/de.xml +++ b/SafeExamBrowser.I18n/Data/de.xml @@ -99,6 +99,21 @@ Build + + Passwort: + + + Benutzername: + + + Benutzername (optional): + + + Bitte geben Sie die erforderlichen Anmeldeinformationen ein, um eine Verbindung mit dem drahtlosen Netzwerk "%%_NAME_%%" herzustellen. + + + Netzwerk-Authentifizierung erforderlich + Abbrechen diff --git a/SafeExamBrowser.I18n/Data/en.xml b/SafeExamBrowser.I18n/Data/en.xml index 03e66143..9700fabc 100644 --- a/SafeExamBrowser.I18n/Data/en.xml +++ b/SafeExamBrowser.I18n/Data/en.xml @@ -99,6 +99,21 @@ Build + + Password: + + + Username: + + + Username (optional): + + + Please enter the required credentials to establish a connection to the wireless network "%%_NAME_%%". + + + Network Authentication Required + Cancel diff --git a/SafeExamBrowser.I18n/Data/es.xml b/SafeExamBrowser.I18n/Data/es.xml index 5bd5d882..f4f24a5a 100644 --- a/SafeExamBrowser.I18n/Data/es.xml +++ b/SafeExamBrowser.I18n/Data/es.xml @@ -99,6 +99,21 @@ Build + + Contraseña: + + + Nombre de usuario: + + + Nombre de usuario (opcional): + + + Por favor, introduzca las credenciales necesarias para establecer una conexión con la red inalámbrica "%%_NAME_%%". + + + Autenticación de red necesaria + Cancel diff --git a/SafeExamBrowser.I18n/Data/et.xml b/SafeExamBrowser.I18n/Data/et.xml index 2766190b..35ef2f74 100644 --- a/SafeExamBrowser.I18n/Data/et.xml +++ b/SafeExamBrowser.I18n/Data/et.xml @@ -99,6 +99,21 @@ Koosta + + Parool: + + + Kasutajanimi: + + + Kasutajanimi (vabatahtlik): + + + Palun sisestage vajalikud volitused, et luua ühendus traadita võrguga "%%_NAME_%%". + + + Võrguautentimine nõutav + Tühista diff --git a/SafeExamBrowser.I18n/Data/fr.xml b/SafeExamBrowser.I18n/Data/fr.xml index 211a1127..a4ce8e44 100644 --- a/SafeExamBrowser.I18n/Data/fr.xml +++ b/SafeExamBrowser.I18n/Data/fr.xml @@ -99,6 +99,21 @@ Build + + Mot de passe: + + + Nom d'utilisateur: + + + Nom d'utilisateur (facultatif): + + + Veuillez saisir les informations d'identification requises pour établir une connexion au réseau sans fil "%%_NAME_%%". + + + Authentification réseau requise + Annuler diff --git a/SafeExamBrowser.I18n/Data/id.xml b/SafeExamBrowser.I18n/Data/id.xml index f3fb69bc..f921fbec 100644 --- a/SafeExamBrowser.I18n/Data/id.xml +++ b/SafeExamBrowser.I18n/Data/id.xml @@ -99,6 +99,21 @@ Bangun + + Kata sandi: + + + Nama pengguna: + + + Nama pengguna (opsional): + + + Masukkan kredensial yang diperlukan untuk membuat sambungan ke jaringan nirkabel "%%_NAMA_%%". + + + Diperlukan Otentikasi Jaringan + Batal diff --git a/SafeExamBrowser.I18n/Data/it.xml b/SafeExamBrowser.I18n/Data/it.xml index 35c157e1..ec33bbde 100644 --- a/SafeExamBrowser.I18n/Data/it.xml +++ b/SafeExamBrowser.I18n/Data/it.xml @@ -99,6 +99,21 @@ Build + + Password: + + + Nome utente: + + + Nome utente (facoltativo): + + + Inserire le credenziali richieste per stabilire una connessione alla rete wireless "%%_NAME_%%". + + + È richiesta l'autenticazione di rete + Annulla diff --git a/SafeExamBrowser.I18n/Data/nl.xml b/SafeExamBrowser.I18n/Data/nl.xml index 93360459..54def0c2 100644 --- a/SafeExamBrowser.I18n/Data/nl.xml +++ b/SafeExamBrowser.I18n/Data/nl.xml @@ -99,6 +99,21 @@ Build + + Wachtwoord: + + + Gebruikersnaam: + + + Gebruikersnaam (optioneel): + + + Voer de vereiste gegevens in om verbinding te maken met het draadloze netwerk “%%_NAME_%%”. + + + Netwerkverificatie vereist + Annuleren diff --git a/SafeExamBrowser.I18n/Data/ru.xml b/SafeExamBrowser.I18n/Data/ru.xml index 6ca6eb25..8651db4a 100644 --- a/SafeExamBrowser.I18n/Data/ru.xml +++ b/SafeExamBrowser.I18n/Data/ru.xml @@ -99,6 +99,21 @@ Строить + + Пароль: + + + Имя пользователя: + + + Имя пользователя (необязательно): + + + Введите необходимые учетные данные для установления соединения с беспроводной сетью "%%_NAME_%%". + + + Требуется сетевая аутентификация + Отмена diff --git a/SafeExamBrowser.I18n/Data/zh.xml b/SafeExamBrowser.I18n/Data/zh.xml index 387ffaf2..2456befd 100644 --- a/SafeExamBrowser.I18n/Data/zh.xml +++ b/SafeExamBrowser.I18n/Data/zh.xml @@ -99,6 +99,21 @@ 生成 + + 密码: + + + 用户名: + + + 用户名(可选): + + + 请输入与无线网络 "%%_NAME_%%" 建立连接所需的凭证。 + + + 需要网络验证 + 取消 diff --git a/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventArgs.cs b/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventArgs.cs index 111aff92..11a4ee63 100644 --- a/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventArgs.cs +++ b/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventArgs.cs @@ -9,22 +9,27 @@ namespace SafeExamBrowser.SystemComponents.Contracts.Network.Events { /// - /// + /// The event arguments for the . /// public class CredentialsRequiredEventArgs { /// - /// + /// The name of the network which requires credentials. + /// + public string NetworkName { get; set; } + + /// + /// The password as specified by the user. /// public string Password { get; set; } /// - /// + /// Indicates whether the credentials could be successfully retrieved or not. /// public bool Success { get; set; } /// - /// + /// The username as specified by the user. /// public string Username { get; set; } } diff --git a/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventHandler.cs b/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventHandler.cs index 888d97a6..08357bed 100644 --- a/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventHandler.cs +++ b/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventHandler.cs @@ -9,7 +9,7 @@ namespace SafeExamBrowser.SystemComponents.Contracts.Network.Events { /// - /// + /// Indicates that credentials are required to connect to a network. /// public delegate void CredentialsRequiredEventHandler(CredentialsRequiredEventArgs args); } diff --git a/SafeExamBrowser.SystemComponents.Contracts/Network/INetworkAdapter.cs b/SafeExamBrowser.SystemComponents.Contracts/Network/INetworkAdapter.cs index 207397cf..ef98a539 100644 --- a/SafeExamBrowser.SystemComponents.Contracts/Network/INetworkAdapter.cs +++ b/SafeExamBrowser.SystemComponents.Contracts/Network/INetworkAdapter.cs @@ -45,5 +45,15 @@ namespace SafeExamBrowser.SystemComponents.Contracts.Network /// Retrieves all currently available wireless networks. /// IEnumerable GetWirelessNetworks(); + + /// + /// Starts periodically scanning the available wireless networks. + /// + void StartWirelessNetworkScanning(); + + /// + /// Stops the periodical scanning of wireless networks. + /// + void StopWirelessNetworkScanning(); } } diff --git a/SafeExamBrowser.SystemComponents/Network/Extensions.cs b/SafeExamBrowser.SystemComponents/Network/Extensions.cs new file mode 100644 index 00000000..01590732 --- /dev/null +++ b/SafeExamBrowser.SystemComponents/Network/Extensions.cs @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 ETH Zürich, IT Services + * + * 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; +using SafeExamBrowser.SystemComponents.Contracts.Network; +using Windows.Devices.WiFi; + +namespace SafeExamBrowser.SystemComponents.Network +{ + internal static class Extensions + { + internal static IOrderedEnumerable FilterAndOrder(this IReadOnlyList networks) + { + return networks.Where(n => !string.IsNullOrEmpty(n.Ssid)).GroupBy(n => n.Ssid).Select(g => g.First()).OrderBy(n => n.Ssid); + } + + internal static WirelessNetwork ToWirelessNetwork(this WiFiAvailableNetwork network) + { + return new WirelessNetwork + { + Name = network.Ssid, + Network = network, + SignalStrength = Convert.ToInt32(Math.Max(0, Math.Min(100, (network.NetworkRssiInDecibelMilliwatts + 100) * 2))), + Status = ConnectionStatus.Disconnected + }; + } + } +} diff --git a/SafeExamBrowser.SystemComponents/Network/NetworkAdapter.cs b/SafeExamBrowser.SystemComponents/Network/NetworkAdapter.cs index 124cfadc..144b364a 100644 --- a/SafeExamBrowser.SystemComponents/Network/NetworkAdapter.cs +++ b/SafeExamBrowser.SystemComponents/Network/NetworkAdapter.cs @@ -14,21 +14,15 @@ using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.SystemComponents.Contracts.Network; using SafeExamBrowser.SystemComponents.Contracts.Network.Events; using SafeExamBrowser.WindowsApi.Contracts; -using SimpleWifi; -using SimpleWifi.Win32; -using SimpleWifi.Win32.Interop; +using Windows.Devices.Enumeration; +using Windows.Devices.WiFi; +using Windows.Foundation; +using Windows.Networking.Connectivity; +using Windows.Security.Credentials; using Timer = System.Timers.Timer; namespace SafeExamBrowser.SystemComponents.Network { - /// - /// Switch to the following WiFi library: - /// https://github.com/emoacht/ManagedNativeWifi - /// https://www.nuget.org/packages/ManagedNativeWifi - /// - /// Potentially useful: - /// https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.networkinterface?view=netframework-4.8 - /// public class NetworkAdapter : INetworkAdapter { private readonly object @lock = new object(); @@ -38,7 +32,9 @@ namespace SafeExamBrowser.SystemComponents.Network private readonly List wirelessNetworks; private Timer timer; - private Wifi wifi; + private WiFiAdapter adapter; + + private bool HasWirelessAdapter => adapter != default; public ConnectionStatus Status { get; private set; } public ConnectionType Type { get; private set; } @@ -55,36 +51,36 @@ namespace SafeExamBrowser.SystemComponents.Network public void ConnectToWirelessNetwork(string name) { + var network = default(WiFiAvailableNetwork); + lock (@lock) { - var network = wirelessNetworks.FirstOrDefault(n => n.Name == name); + network = wirelessNetworks.FirstOrDefault(n => n.Name == name)?.Network; + } - if (network != default) + if (network != default) + { + var isOpen = network.SecuritySettings.NetworkAuthenticationType == NetworkAuthenticationType.Open80211 && network.SecuritySettings.NetworkEncryptionType == NetworkEncryptionType.None; + + if (isOpen) { - try - { - var accessPoint = network.AccessPoint; - var request = new AuthRequest(accessPoint); + logger.Info($"Attempting to connect to open wireless network '{name}'..."); - if (accessPoint.HasProfile || accessPoint.IsConnected || TryGetCredentials(request)) - { - logger.Info($"Attempting to connect to wireless network '{network.Name}' with{(request.Password == default ? "out" : "")} credentials..."); - - // TODO: Retry resp. alert of password error on failure and then ignore profile?! - accessPoint.ConnectAsync(request, false, (success) => ConnectionAttemptCompleted(network.Name, success)); - Status = ConnectionStatus.Connecting; - } - } - catch (Exception e) - { - logger.Error($"Failed to connect to wireless network '{network.Name}!'", e); - } + adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic).Completed = (o, s) => Adapter_ConnectCompleted(name, o, s); + Status = ConnectionStatus.Connecting; } - else + else if (TryGetCredentials(name, out var credentials)) { - logger.Warn($"Could not find wireless network '{name}'!"); + logger.Info($"Attempting to connect to wireless network '{name}' with credentials..."); + + adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic, credentials).Completed = (o, s) => Adapter_ConnectCompleted(name, o, s); + Status = ConnectionStatus.Connecting; } } + else + { + logger.Warn($"Could not find wireless network '{name}'!"); + } Changed?.Invoke(); } @@ -101,103 +97,233 @@ namespace SafeExamBrowser.SystemComponents.Network { const int FIVE_SECONDS = 5000; - NetworkChange.NetworkAddressChanged += (o, args) => Update(); - NetworkChange.NetworkAvailabilityChanged += (o, args) => Update(); - - wifi = new Wifi(); - wifi.ConnectionStatusChanged += (o, args) => Update(); - timer = new Timer(FIVE_SECONDS); timer.Elapsed += (o, args) => Update(); timer.AutoReset = true; - timer.Start(); + + InitializeAdapter(); + + NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged; + NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged; + NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged; Update(); logger.Info("Started monitoring the network adapter."); } - public void Terminate() + public void StartWirelessNetworkScanning() { - if (timer != null) + timer?.Start(); + + if (HasWirelessAdapter) { - timer.Stop(); - logger.Info("Stopped monitoring the network adapter."); + _ = adapter.ScanAsync(); } } - private void ConnectionAttemptCompleted(string name, bool success) + public void StopWirelessNetworkScanning() { - lock (@lock) + timer?.Stop(); + } + + public void Terminate() + { + NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged; + NetworkChange.NetworkAvailabilityChanged -= NetworkChange_NetworkAvailabilityChanged; + NetworkInformation.NetworkStatusChanged -= NetworkInformation_NetworkStatusChanged; + + if (HasWirelessAdapter) { - // This handler seems to be called before the connection has been fully established, thus we don't yet set the status to connected... - Status = success ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected; + adapter.AvailableNetworksChanged -= Adapter_AvailableNetworksChanged; } - if (success) + if (timer != default) { + timer.Stop(); + } + + logger.Info("Stopped monitoring the network adapter."); + } + + private void InitializeAdapter() + { + try + { + // Requesting access is required as of fall 2024 and must be granted manually by the user, otherwise all wireless functionality will + // be denied by the system (see also https://learn.microsoft.com/en-us/windows/win32/nativewifi/wi-fi-access-location-changes). + var task = WiFiAdapter.RequestAccessAsync().AsTask(); + var status = task.GetAwaiter().GetResult(); + + if (status == WiFiAccessStatus.Allowed) + { + var findAll = DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector()).AsTask(); + var devices = findAll.GetAwaiter().GetResult(); + + if (devices.Any()) + { + var id = devices.First().Id; + var getById = WiFiAdapter.FromIdAsync(id).AsTask(); + + logger.Debug($"Found {devices.Count()} wireless network adapter(s)."); + + adapter = getById.GetAwaiter().GetResult(); + adapter.AvailableNetworksChanged += Adapter_AvailableNetworksChanged; + + logger.Debug($"Successfully initialized wireless network adapter '{id}'."); + } + else + { + logger.Info("Could not find a wireless network adapter."); + } + } + else + { + logger.Error($"Access to the wireless network adapter has been denied ({status})!"); + } + } + catch (Exception e) + { + logger.Error("Failed to initialize wireless network adapter!", e); + } + } + + private void Adapter_AvailableNetworksChanged(WiFiAdapter sender, object args) + { + Update(false); + } + + private void Adapter_ConnectCompleted(string name, IAsyncOperation operation, AsyncStatus status) + { + var connectionStatus = default(WiFiConnectionStatus?); + + if (status == AsyncStatus.Completed) + { + connectionStatus = operation.GetResults()?.ConnectionStatus; + } + else + { + logger.Error($"Failed to complete connection operation! Status: {status}."); + } + + if (connectionStatus == WiFiConnectionStatus.Success) + { + Status = ConnectionStatus.Connected; logger.Info($"Successfully connected to wireless network '{name}'."); } else { - logger.Error($"Failed to connect to wireless network '{name}!'"); + Status = ConnectionStatus.Disconnected; + logger.Error($"Failed to connect to wireless network '{name}'! Reason: {connectionStatus}."); } + + Update(); } - private bool TryGetCredentials(AuthRequest request) + private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) { - var args = new CredentialsRequiredEventArgs(); + logger.Debug("Network address changed."); + Update(); + } + + private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) + { + logger.Debug($"Network availability changed ({(e.IsAvailable ? "available" : "unavailable")}."); + Update(); + } + + private void NetworkInformation_NetworkStatusChanged(object sender) + { + logger.Debug("Network status changed."); + Update(); + } + + private bool TryGetCredentials(string network, out PasswordCredential credentials) + { + var args = new CredentialsRequiredEventArgs { NetworkName = network }; + + credentials = new PasswordCredential(); CredentialsRequired?.Invoke(args); if (args.Success) { - request.Password = args.Password; - request.Username = args.Username; + if (!string.IsNullOrEmpty(args.Password)) + { + credentials.Password = args.Password; + } + + if (!string.IsNullOrEmpty(args.Username)) + { + credentials.UserName = args.Username; + } } return args.Success; } - private void Update() + private bool TryGetCurrentWirelessNetwork(out string name) + { + name = default; + + if (HasWirelessAdapter) + { + try + { + var getProfile = adapter.NetworkAdapter.GetConnectedProfileAsync().AsTask(); + var profile = getProfile.GetAwaiter().GetResult(); + + if (profile?.IsWlanConnectionProfile == true) + { + name = profile.WlanConnectionProfileDetails.GetConnectedSsid(); + } + } + catch + { + } + } + + return name != default; + } + + private void Update(bool rescan = true) { try { lock (@lock) { - var current = default(WirelessNetwork); var hasInternet = nativeMethods.HasInternetConnection(); - var hasWireless = !wifi.NoWifiAvailable && !IsTurnedOff(); var isConnecting = Status == ConnectionStatus.Connecting; var previousStatus = Status; wirelessNetworks.Clear(); - if (hasWireless) + if (HasWirelessAdapter) { - foreach (var wirelessNetwork in wifi.GetAccessPoints().Select(a => ToWirelessNetwork(a))) - { - wirelessNetworks.Add(wirelessNetwork); + TryGetCurrentWirelessNetwork(out var currentNetwork); - if (wirelessNetwork.Status == ConnectionStatus.Connected) + foreach (var network in adapter.NetworkReport.AvailableNetworks.FilterAndOrder()) + { + var wirelessNetwork = network.ToWirelessNetwork(); + + if (network.Ssid == currentNetwork) { - current = wirelessNetwork; + wirelessNetwork.Status = ConnectionStatus.Connected; } + + wirelessNetworks.Add(wirelessNetwork); + } + + if (rescan) + { + _ = adapter.ScanAsync(); } } - Type = hasWireless ? ConnectionType.Wireless : (hasInternet ? ConnectionType.Wired : ConnectionType.Undefined); - Status = hasInternet ? ConnectionStatus.Connected : (hasWireless && isConnecting ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected); + Type = HasWirelessAdapter ? ConnectionType.Wireless : (hasInternet ? ConnectionType.Wired : ConnectionType.Undefined); + Status = hasInternet ? ConnectionStatus.Connected : (HasWirelessAdapter && isConnecting ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected); - if (previousStatus != ConnectionStatus.Connected && Status == ConnectionStatus.Connected) - { - logger.Info($"Connection established ({Type}{(current != default ? $", {current.Name}" : "")})."); - } - - if (previousStatus != ConnectionStatus.Disconnected && Status == ConnectionStatus.Disconnected) - { - logger.Info("Connection lost."); - } + LogNetworkChanges(previousStatus, wirelessNetworks.FirstOrDefault(n => n.Status == ConnectionStatus.Connected)); } } catch (Exception e) @@ -208,40 +334,17 @@ namespace SafeExamBrowser.SystemComponents.Network Changed?.Invoke(); } - private bool IsTurnedOff() + private void LogNetworkChanges(ConnectionStatus previousStatus, WirelessNetwork currentNetwork) { - try + if (previousStatus != ConnectionStatus.Connected && Status == ConnectionStatus.Connected) { - var client = new WlanClient(); - - foreach (var @interface in client.Interfaces) - { - foreach (var state in @interface.RadioState.PhyRadioState) - { - if (state.dot11SoftwareRadioState == Dot11RadioState.On && state.dot11HardwareRadioState == Dot11RadioState.On) - { - return false; - } - } - } - } - catch (Exception e) - { - logger.Error("Failed to determine the radio state of the wireless adapter(s)! Assuming it is (all are) turned off...", e); + logger.Info($"Connection established ({Type}{(currentNetwork != default ? $", {currentNetwork.Name}" : "")})."); } - return true; - } - - private WirelessNetwork ToWirelessNetwork(AccessPoint accessPoint) - { - return new WirelessNetwork + if (previousStatus != ConnectionStatus.Disconnected && Status == ConnectionStatus.Disconnected) { - AccessPoint = accessPoint, - Name = accessPoint.Name, - SignalStrength = Convert.ToInt32(accessPoint.SignalStrength), - Status = accessPoint.IsConnected ? ConnectionStatus.Connected : ConnectionStatus.Disconnected - }; + logger.Info("Connection lost."); + } } } } diff --git a/SafeExamBrowser.SystemComponents/Network/WirelessNetwork.cs b/SafeExamBrowser.SystemComponents/Network/WirelessNetwork.cs index 35bce4cf..4dca409f 100644 --- a/SafeExamBrowser.SystemComponents/Network/WirelessNetwork.cs +++ b/SafeExamBrowser.SystemComponents/Network/WirelessNetwork.cs @@ -7,13 +7,13 @@ */ using SafeExamBrowser.SystemComponents.Contracts.Network; -using SimpleWifi; +using Windows.Devices.WiFi; namespace SafeExamBrowser.SystemComponents.Network { internal class WirelessNetwork : IWirelessNetwork { - internal AccessPoint AccessPoint { get; set; } + internal WiFiAvailableNetwork Network { get; set; } public string Name { get; set; } public int SignalStrength { get; set; } diff --git a/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs b/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs index 3d693af0..8a2805c4 100644 --- a/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs +++ b/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs @@ -58,12 +58,12 @@ namespace SafeExamBrowser.SystemComponents.PowerSupply public void Initialize() { - const int TWO_SECONDS = 2000; + const int FIVE_SECONDS = 5000; critical = SanitizeThreshold(settings.ChargeThresholdCritical); low = SanitizeThreshold(settings.ChargeThresholdLow); - timer = new Timer(TWO_SECONDS); + timer = new Timer(FIVE_SECONDS); timer.Elapsed += Timer_Elapsed; timer.AutoReset = true; timer.Start(); diff --git a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj index ec0ae697..4e783d5e 100644 --- a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj +++ b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj @@ -49,44 +49,10 @@ MinimumRecommendedRules.ruleset - - ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll - - - ..\packages\NAudio.2.2.1\lib\net472\NAudio.dll - - - ..\packages\NAudio.Asio.2.2.1\lib\netstandard2.0\NAudio.Asio.dll - - - ..\packages\NAudio.Core.2.2.1\lib\netstandard2.0\NAudio.Core.dll - - - ..\packages\NAudio.Midi.2.2.1\lib\netstandard2.0\NAudio.Midi.dll - - - ..\packages\NAudio.Wasapi.2.2.1\lib\netstandard2.0\NAudio.Wasapi.dll - - - ..\packages\NAudio.WinForms.2.2.1\lib\net472\NAudio.WinForms.dll - - - ..\packages\NAudio.WinMM.2.2.1\lib\netstandard2.0\NAudio.WinMM.dll - - - False - ..\Libraries\SimpleWifi.dll - - - ..\packages\System.Security.AccessControl.6.0.0\lib\net461\System.Security.AccessControl.dll - - - ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll - @@ -94,6 +60,7 @@ + @@ -125,7 +92,20 @@ - + + + + 5.0.0 + + + 10.0.17134.1000 + + + 2.2.1 + + + 6.0.1 + \ No newline at end of file diff --git a/SafeExamBrowser.SystemComponents/packages.config b/SafeExamBrowser.SystemComponents/packages.config deleted file mode 100644 index 22ee678a..00000000 --- a/SafeExamBrowser.SystemComponents/packages.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs index 94825649..21bf2a80 100644 --- a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs @@ -58,6 +58,11 @@ namespace SafeExamBrowser.UserInterface.Contracts /// IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow, ILogger logger); + /// + /// Creates a credentials dialog for the given purpose and with the specified message and title. + /// + ICredentialsDialog CreateCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title); + /// /// Creates an exam selection dialog for the given exams. /// @@ -83,11 +88,6 @@ namespace SafeExamBrowser.UserInterface.Contracts /// ISystemControl CreateNetworkControl(INetworkAdapter adapter, Location location); - /// - /// Creates a network dialog with the given message and title. - /// - INetworkDialog CreateNetworkDialog(string message, string title); - /// /// Creates a notification control for the given notification, initialized for the specified location. /// diff --git a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj index 698d2010..cf090763 100644 --- a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj +++ b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj @@ -93,16 +93,17 @@ + - + - + diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogPurpose.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogPurpose.cs new file mode 100644 index 00000000..5f7686ec --- /dev/null +++ b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogPurpose.cs @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 ETH Zürich, IT Services + * + * 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.UserInterface.Contracts.Windows.Data +{ + /// + /// Defines the purpose of a . + /// + public enum CredentialsDialogPurpose + { + /// + /// Credentials for generic purposes. + /// + Generic, + + /// + /// Credentials for wireless network authentication. + /// + WirelessNetwork + } +} diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/NetworkDialogResult.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogResult.cs similarity index 88% rename from SafeExamBrowser.UserInterface.Contracts/Windows/Data/NetworkDialogResult.cs rename to SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogResult.cs index 439814d4..d2686039 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/NetworkDialogResult.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogResult.cs @@ -9,9 +9,9 @@ namespace SafeExamBrowser.UserInterface.Contracts.Windows.Data { /// - /// Defines the user interaction result of an . + /// Defines the user interaction result of an . /// - public class NetworkDialogResult + public class CredentialsDialogResult { /// /// The password entered by the user, or default(string) if the interaction was unsuccessful. diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/INetworkDialog.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/ICredentialsDialog.cs similarity index 76% rename from SafeExamBrowser.UserInterface.Contracts/Windows/INetworkDialog.cs rename to SafeExamBrowser.UserInterface.Contracts/Windows/ICredentialsDialog.cs index a5c347e5..b2b3ff8b 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Windows/INetworkDialog.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Windows/ICredentialsDialog.cs @@ -11,13 +11,13 @@ using SafeExamBrowser.UserInterface.Contracts.Windows.Data; namespace SafeExamBrowser.UserInterface.Contracts.Windows { /// - /// Defines the functionality of a network dialog. + /// Defines the functionality of a dialog to retrieve user credentials. /// - public interface INetworkDialog : IWindow + public interface ICredentialsDialog : IWindow { /// /// Shows the dialog as topmost window. If a parent window is specified, the dialog is rendered modally for the given parent. /// - NetworkDialogResult Show(IWindow parent = null); + CredentialsDialogResult Show(IWindow parent = null); } } diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NetworkControl.xaml.cs index 72c03e1c..ee636b9c 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NetworkControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NetworkControl.xaml.cs @@ -63,6 +63,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter } Popup.IsOpen = Popup.IsMouseOver; })); + Popup.Closed += (o, args) => + { + adapter.StopWirelessNetworkScanning(); + Grid.Background = originalBrush; + lastOpenedBySpacePress = false; + }; Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => { if (Popup.IsOpen && lastOpenedBySpacePress) @@ -73,6 +79,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter })); Popup.Opened += (o, args) => { + adapter.StartWirelessNetworkScanning(); Grid.Background = Brushes.Gray; Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() => { @@ -82,11 +89,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter } })); }; - Popup.Closed += (o, args) => - { - Grid.Background = originalBrush; - lastOpenedBySpacePress = false; - }; WirelessIcon.Child = GetWirelessIcon(0); Update(); @@ -94,23 +96,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter private void Update() { - WirelessNetworksStackPanel.Children.Clear(); - - foreach (var network in adapter.GetWirelessNetworks()) - { - var button = new NetworkButton(network); - - button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name); - - if (network.Status == ConnectionStatus.Connected) - { - WirelessIcon.Child = GetWirelessIcon(network.SignalStrength); - UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name)); - } - - WirelessNetworksStackPanel.Children.Add(button); - } - switch (adapter.Type) { case ConnectionType.Wired: @@ -154,6 +139,23 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter WirelessIcon.Child = GetWirelessIcon(0); break; } + + WirelessNetworksStackPanel.Children.Clear(); + + foreach (var network in adapter.GetWirelessNetworks()) + { + var button = new NetworkButton(network); + + button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name); + + if (network.Status == ConnectionStatus.Connected) + { + WirelessIcon.Child = GetWirelessIcon(network.SignalStrength); + UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name)); + } + + WirelessNetworksStackPanel.Children.Add(button); + } } private void UpdateText(string text) diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NetworkControl.xaml.cs index 8f058b05..1d2cf1c2 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NetworkControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NetworkControl.xaml.cs @@ -66,6 +66,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar })); Popup.Closed += (o, args) => { + adapter.StopWirelessNetworkScanning(); Background = originalBrush; Button.Background = originalBrush; lastOpenedBySpacePress = false; @@ -81,6 +82,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar })); Popup.Opened += (o, args) => { + adapter.StartWirelessNetworkScanning(); Background = Brushes.LightGray; Button.Background = Brushes.LightGray; Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() => @@ -106,23 +108,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar private void Update() { - WirelessNetworksStackPanel.Children.Clear(); - - foreach (var network in adapter.GetWirelessNetworks()) - { - var button = new NetworkButton(network); - - button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name); - - if (network.Status == ConnectionStatus.Connected) - { - WirelessIcon.Child = GetWirelessIcon(network.SignalStrength); - UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name)); - } - - WirelessNetworksStackPanel.Children.Add(button); - } - switch (adapter.Type) { case ConnectionType.Wired: @@ -166,6 +151,23 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar WirelessIcon.Child = GetWirelessIcon(0); break; } + + WirelessNetworksStackPanel.Children.Clear(); + + foreach (var network in adapter.GetWirelessNetworks()) + { + var button = new NetworkButton(network); + + button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name); + + if (network.Status == ConnectionStatus.Connected) + { + WirelessIcon.Child = GetWirelessIcon(network.SignalStrength); + UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name)); + } + + WirelessNetworksStackPanel.Children.Add(button); + } } private void UpdateText(string text) diff --git a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj index e9e76f25..d67030bc 100644 --- a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj +++ b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj @@ -168,8 +168,8 @@ LogWindow.xaml - - NetworkDialog.xaml + + CredentialsDialog.xaml PasswordDialog.xaml @@ -397,7 +397,7 @@ Designer MSBuild:Compile - + Designer MSBuild:Compile diff --git a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs index 597e7840..a1019d80 100644 --- a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs @@ -85,6 +85,11 @@ namespace SafeExamBrowser.UserInterface.Desktop return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text, logger)); } + public ICredentialsDialog CreateCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title) + { + return Application.Current.Dispatcher.Invoke(() => new CredentialsDialog(purpose, message, title, text)); + } + public IExamSelectionDialog CreateExamSelectionDialog(IEnumerable exams) { return Application.Current.Dispatcher.Invoke(() => new ExamSelectionDialog(exams, text)); @@ -143,11 +148,6 @@ namespace SafeExamBrowser.UserInterface.Desktop } } - public INetworkDialog CreateNetworkDialog(string message, string title) - { - return Application.Current.Dispatcher.Invoke(() => new NetworkDialog(message, title, text)); - } - public INotificationControl CreateNotificationControl(INotification notification, Location location) { if (location == Location.ActionCenter) diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/NetworkDialog.xaml b/SafeExamBrowser.UserInterface.Desktop/Windows/CredentialsDialog.xaml similarity index 76% rename from SafeExamBrowser.UserInterface.Desktop/Windows/NetworkDialog.xaml rename to SafeExamBrowser.UserInterface.Desktop/Windows/CredentialsDialog.xaml index 29a4c611..ee646bd0 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/NetworkDialog.xaml +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/CredentialsDialog.xaml @@ -1,11 +1,11 @@ - + mc:Ignorable="d" Height="325" Width="450" ResizeMode="NoResize" Topmost="True"> @@ -15,17 +15,17 @@ - - + + - + - - + + @@ -33,18 +33,18 @@ - - - +