SEBWIN-833: Completely deleted all Jitsi Meet and Zoom video proctoring code and removed WebView2 dependency.

This commit is contained in:
Damian Büchel 2024-04-17 09:19:18 +02:00
parent d9662ec31e
commit e8ebd2840e
29 changed files with 39 additions and 1511 deletions

View file

@ -21,60 +21,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
case Keys.Proctoring.ForceRaiseHandMessage:
MapForceRaiseHandMessage(settings, value);
break;
case Keys.Proctoring.JitsiMeet.AllowChat:
MapJitsiMeetAllowChat(settings, value);
break;
case Keys.Proctoring.JitsiMeet.AllowClosedCaptions:
MapJitsiMeetAllowClosedCaptions(settings, value);
break;
case Keys.Proctoring.JitsiMeet.AllowRaiseHand:
MapJitsiMeetAllowRaiseHands(settings, value);
break;
case Keys.Proctoring.JitsiMeet.AllowRecording:
MapJitsiMeetAllowRecording(settings, value);
break;
case Keys.Proctoring.JitsiMeet.AllowTileView:
MapJitsiMeetAllowTileView(settings, value);
break;
case Keys.Proctoring.JitsiMeet.AudioMuted:
MapJitsiMeetAudioMuted(settings, value);
break;
case Keys.Proctoring.JitsiMeet.AudioOnly:
MapJitsiMeetAudioOnly(settings, value);
break;
case Keys.Proctoring.JitsiMeet.Enabled:
MapJitsiMeetEnabled(settings, value);
break;
case Keys.Proctoring.JitsiMeet.ReceiveAudio:
MapJitsiMeetReceiveAudio(settings, value);
break;
case Keys.Proctoring.JitsiMeet.ReceiveVideo:
MapJitsiMeetReceiveVideo(settings, value);
break;
case Keys.Proctoring.JitsiMeet.RoomName:
MapJitsiMeetRoomName(settings, value);
break;
case Keys.Proctoring.JitsiMeet.SendAudio:
MapJitsiMeetSendAudio(settings, value);
break;
case Keys.Proctoring.JitsiMeet.SendVideo:
MapJitsiMeetSendVideo(settings, value);
break;
case Keys.Proctoring.JitsiMeet.ServerUrl:
MapJitsiMeetServerUrl(settings, value);
break;
case Keys.Proctoring.JitsiMeet.ShowMeetingName:
MapJitsiMeetShowMeetingName(settings, value);
break;
case Keys.Proctoring.JitsiMeet.Subject:
MapJitsiMeetSubject(settings, value);
break;
case Keys.Proctoring.JitsiMeet.Token:
MapJitsiMeetToken(settings, value);
break;
case Keys.Proctoring.JitsiMeet.VideoMuted:
MapJitsiMeetVideoMuted(settings, value);
break;
case Keys.Proctoring.ScreenProctoring.ClientId:
MapClientId(settings, value);
break;
@ -120,51 +66,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
case Keys.Proctoring.ShowTaskbarNotification:
MapShowTaskbarNotification(settings, value);
break;
case Keys.Proctoring.WindowVisibility:
MapWindowVisibility(settings, value);
break;
case Keys.Proctoring.Zoom.AllowChat:
MapZoomAllowChat(settings, value);
break;
case Keys.Proctoring.Zoom.AllowClosedCaptions:
MapZoomAllowClosedCaptions(settings, value);
break;
case Keys.Proctoring.Zoom.AllowRaiseHand:
MapZoomAllowRaiseHands(settings, value);
break;
case Keys.Proctoring.Zoom.AudioMuted:
MapZoomAudioMuted(settings, value);
break;
case Keys.Proctoring.Zoom.Enabled:
MapZoomEnabled(settings, value);
break;
case Keys.Proctoring.Zoom.MeetingNumber:
MapZoomMeetingNumber(settings, value);
break;
case Keys.Proctoring.Zoom.ReceiveAudio:
MapZoomReceiveAudio(settings, value);
break;
case Keys.Proctoring.Zoom.ReceiveVideo:
MapZoomReceiveVideo(settings, value);
break;
case Keys.Proctoring.Zoom.SendAudio:
MapZoomSendAudio(settings, value);
break;
case Keys.Proctoring.Zoom.SendVideo:
MapZoomSendVideo(settings, value);
break;
case Keys.Proctoring.Zoom.Signature:
MapZoomSignature(settings, value);
break;
case Keys.Proctoring.Zoom.Subject:
MapZoomSubject(settings, value);
break;
case Keys.Proctoring.Zoom.UserName:
MapZoomUserName(settings, value);
break;
case Keys.Proctoring.Zoom.VideoMuted:
MapZoomVideoMuted(settings, value);
break;
}
}
@ -176,150 +77,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
}
}
private void MapJitsiMeetAllowChat(AppSettings settings, object value)
{
if (value is bool allow)
{
settings.Proctoring.JitsiMeet.AllowChat = allow;
}
}
private void MapJitsiMeetAllowClosedCaptions(AppSettings settings, object value)
{
if (value is bool allow)
{
settings.Proctoring.JitsiMeet.AllowClosedCaptions = allow;
}
}
private void MapJitsiMeetAllowRaiseHands(AppSettings settings, object value)
{
if (value is bool allow)
{
settings.Proctoring.JitsiMeet.AllowRaiseHand = allow;
}
}
private void MapJitsiMeetAllowRecording(AppSettings settings, object value)
{
if (value is bool allow)
{
settings.Proctoring.JitsiMeet.AllowRecording = allow;
}
}
private void MapJitsiMeetAllowTileView(AppSettings settings, object value)
{
if (value is bool allow)
{
settings.Proctoring.JitsiMeet.AllowTileView = allow;
}
}
private void MapJitsiMeetAudioMuted(AppSettings settings, object value)
{
if (value is bool audioMuted)
{
settings.Proctoring.JitsiMeet.AudioMuted = audioMuted;
}
}
private void MapJitsiMeetAudioOnly(AppSettings settings, object value)
{
if (value is bool audioOnly)
{
settings.Proctoring.JitsiMeet.AudioOnly = audioOnly;
}
}
private void MapJitsiMeetEnabled(AppSettings settings, object value)
{
if (value is bool enabled)
{
settings.Proctoring.JitsiMeet.Enabled = enabled;
}
}
private void MapJitsiMeetReceiveAudio(AppSettings settings, object value)
{
if (value is bool receive)
{
settings.Proctoring.JitsiMeet.ReceiveAudio = receive;
}
}
private void MapJitsiMeetReceiveVideo(AppSettings settings, object value)
{
if (value is bool receive)
{
settings.Proctoring.JitsiMeet.ReceiveVideo = receive;
}
}
private void MapJitsiMeetRoomName(AppSettings settings, object value)
{
if (value is string name)
{
settings.Proctoring.JitsiMeet.RoomName = name;
}
}
private void MapJitsiMeetSendAudio(AppSettings settings, object value)
{
if (value is bool send)
{
settings.Proctoring.JitsiMeet.SendAudio = send;
}
}
private void MapJitsiMeetSendVideo(AppSettings settings, object value)
{
if (value is bool send)
{
settings.Proctoring.JitsiMeet.SendVideo = send;
}
}
private void MapJitsiMeetServerUrl(AppSettings settings, object value)
{
if (value is string url)
{
settings.Proctoring.JitsiMeet.ServerUrl = url;
}
}
private void MapJitsiMeetShowMeetingName(AppSettings settings, object value)
{
if (value is bool show)
{
settings.Proctoring.JitsiMeet.ShowMeetingName = show;
}
}
private void MapJitsiMeetSubject(AppSettings settings, object value)
{
if (value is string subject)
{
settings.Proctoring.JitsiMeet.Subject = subject;
}
}
private void MapJitsiMeetToken(AppSettings settings, object value)
{
if (value is string token)
{
settings.Proctoring.JitsiMeet.Token = token;
}
}
private void MapJitsiMeetVideoMuted(AppSettings settings, object value)
{
if (value is bool muted)
{
settings.Proctoring.JitsiMeet.VideoMuted = muted;
}
}
private void MapCaptureApplicationData(AppSettings settings, object value)
{
if (value is bool capture)
@ -463,144 +220,5 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
settings.Proctoring.ShowTaskbarNotification = show;
}
}
private void MapWindowVisibility(AppSettings settings, object value)
{
const int HIDDEN = 0;
const int ALLOW_SHOW = 1;
const int ALLOW_HIDE = 2;
const int VISIBLE = 3;
if (value is int visibility)
{
switch (visibility)
{
case HIDDEN:
settings.Proctoring.WindowVisibility = WindowVisibility.Hidden;
break;
case ALLOW_SHOW:
settings.Proctoring.WindowVisibility = WindowVisibility.AllowToShow;
break;
case ALLOW_HIDE:
settings.Proctoring.WindowVisibility = WindowVisibility.AllowToHide;
break;
case VISIBLE:
settings.Proctoring.WindowVisibility = WindowVisibility.Visible;
break;
}
}
}
private void MapZoomAllowChat(AppSettings settings, object value)
{
if (value is bool allow)
{
settings.Proctoring.Zoom.AllowChat = allow;
}
}
private void MapZoomAllowClosedCaptions(AppSettings settings, object value)
{
if (value is bool allow)
{
settings.Proctoring.Zoom.AllowClosedCaptions = allow;
}
}
private void MapZoomAllowRaiseHands(AppSettings settings, object value)
{
if (value is bool allow)
{
settings.Proctoring.Zoom.AllowRaiseHand = allow;
}
}
private void MapZoomAudioMuted(AppSettings settings, object value)
{
if (value is bool muted)
{
settings.Proctoring.Zoom.AudioMuted = muted;
}
}
private void MapZoomEnabled(AppSettings settings, object value)
{
if (value is bool enabled)
{
settings.Proctoring.Zoom.Enabled = enabled;
}
}
private void MapZoomMeetingNumber(AppSettings settings, object value)
{
if (value is string number)
{
settings.Proctoring.Zoom.MeetingNumber = number;
}
}
private void MapZoomReceiveAudio(AppSettings settings, object value)
{
if (value is bool receive)
{
settings.Proctoring.Zoom.ReceiveAudio = receive;
}
}
private void MapZoomReceiveVideo(AppSettings settings, object value)
{
if (value is bool receive)
{
settings.Proctoring.Zoom.ReceiveVideo = receive;
}
}
private void MapZoomSendAudio(AppSettings settings, object value)
{
if (value is bool send)
{
settings.Proctoring.Zoom.SendAudio = send;
}
}
private void MapZoomSendVideo(AppSettings settings, object value)
{
if (value is bool send)
{
settings.Proctoring.Zoom.SendVideo = send;
}
}
private void MapZoomSignature(AppSettings settings, object value)
{
if (value is string signature)
{
settings.Proctoring.Zoom.Signature = signature;
}
}
private void MapZoomSubject(AppSettings settings, object value)
{
if (value is string subject)
{
settings.Proctoring.Zoom.Subject = subject;
}
}
private void MapZoomUserName(AppSettings settings, object value)
{
if (value is string name)
{
settings.Proctoring.Zoom.UserName = name;
}
}
private void MapZoomVideoMuted(AppSettings settings, object value)
{
if (value is bool muted)
{
settings.Proctoring.Zoom.VideoMuted = muted;
}
}
}
}

View file

@ -12,7 +12,6 @@ using System.IO;
using System.Security.Cryptography;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Applications;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.Settings.Security;
namespace SafeExamBrowser.Configuration.ConfigurationData
@ -77,15 +76,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
private void InitializeProctoringSettings(AppSettings settings)
{
settings.Proctoring.Enabled = settings.Proctoring.ScreenProctoring.Enabled;
// The video proctoring implementations are disabled for version 3.7.0 and will be completely removed for version 3.8.0.
settings.Proctoring.JitsiMeet.Enabled = false;
settings.Proctoring.Zoom.Enabled = false;
if (settings.Proctoring.JitsiMeet.Enabled && !settings.Proctoring.JitsiMeet.ReceiveVideo)
{
settings.Proctoring.WindowVisibility = WindowVisibility.Hidden;
}
}
private void RemoveLegacyBrowsers(AppSettings settings)

View file

@ -243,20 +243,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.Proctoring.Enabled = false;
settings.Proctoring.ForceRaiseHandMessage = false;
settings.Proctoring.JitsiMeet.AllowChat = false;
settings.Proctoring.JitsiMeet.AllowClosedCaptions = false;
settings.Proctoring.JitsiMeet.AllowRaiseHand = false;
settings.Proctoring.JitsiMeet.AllowRecording = false;
settings.Proctoring.JitsiMeet.AllowTileView = false;
settings.Proctoring.JitsiMeet.AudioMuted = true;
settings.Proctoring.JitsiMeet.AudioOnly = false;
settings.Proctoring.JitsiMeet.Enabled = false;
settings.Proctoring.JitsiMeet.ReceiveAudio = false;
settings.Proctoring.JitsiMeet.ReceiveVideo = false;
settings.Proctoring.JitsiMeet.SendAudio = true;
settings.Proctoring.JitsiMeet.SendVideo = true;
settings.Proctoring.JitsiMeet.ShowMeetingName = false;
settings.Proctoring.JitsiMeet.VideoMuted = false;
settings.Proctoring.ScreenProctoring.Enabled = false;
settings.Proctoring.ScreenProctoring.ImageDownscaling = 1.0;
settings.Proctoring.ScreenProctoring.ImageFormat = ImageFormat.Png;
@ -268,17 +254,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.Proctoring.ScreenProctoring.MinInterval = 1000;
settings.Proctoring.ShowRaiseHandNotification = true;
settings.Proctoring.ShowTaskbarNotification = true;
settings.Proctoring.WindowVisibility = WindowVisibility.Hidden;
settings.Proctoring.Zoom.AllowChat = false;
settings.Proctoring.Zoom.AllowClosedCaptions = false;
settings.Proctoring.Zoom.AllowRaiseHand = false;
settings.Proctoring.Zoom.AudioMuted = true;
settings.Proctoring.Zoom.Enabled = false;
settings.Proctoring.Zoom.ReceiveAudio = false;
settings.Proctoring.Zoom.ReceiveVideo = false;
settings.Proctoring.Zoom.SendAudio = true;
settings.Proctoring.Zoom.SendVideo = true;
settings.Proctoring.Zoom.VideoMuted = false;
settings.Security.AllowApplicationLogAccess = false;
settings.Security.AllowTermination = true;

View file

@ -234,29 +234,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
internal const string ForceRaiseHandMessage = "raiseHandButtonAlwaysPromptMessage";
internal const string ShowRaiseHand = "raiseHandButtonShow";
internal const string ShowTaskbarNotification = "showProctoringViewButton";
internal const string WindowVisibility = "remoteProctoringViewShow";
internal static class JitsiMeet
{
internal const string AllowChat = "jitsiMeetFeatureFlagChat";
internal const string AllowClosedCaptions = "jitsiMeetFeatureFlagCloseCaptions";
internal const string AllowRaiseHand = "jitsiMeetFeatureFlagRaiseHand";
internal const string AllowRecording = "jitsiMeetFeatureFlagRecording";
internal const string AllowTileView = "jitsiMeetFeatureFlagTileView";
internal const string AudioMuted = "jitsiMeetAudioMuted";
internal const string AudioOnly = "jitsiMeetAudioOnly";
internal const string Enabled = "jitsiMeetEnable";
internal const string ReceiveAudio = "jitsiMeetReceiveAudio";
internal const string ReceiveVideo = "jitsiMeetReceiveVideo";
internal const string RoomName = "jitsiMeetRoom";
internal const string SendAudio = "jitsiMeetSendAudio";
internal const string SendVideo = "jitsiMeetSendVideo";
internal const string ServerUrl = "jitsiMeetServerURL";
internal const string ShowMeetingName = "jitsiMeetFeatureFlagDisplayMeetingName";
internal const string Subject = "jitsiMeetSubject";
internal const string Token = "jitsiMeetToken";
internal const string VideoMuted = "jitsiMeetVideoMuted";
}
internal static class ScreenProctoring
{
@ -278,24 +255,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
internal const string CaptureWindowTitle = "screenProctoringMetadataWindowTitleEnabled";
}
}
internal static class Zoom
{
internal const string AllowChat = "zoomFeatureFlagChat";
internal const string AllowClosedCaptions = "zoomFeatureFlagCloseCaptions";
internal const string AllowRaiseHand = "zoomFeatureFlagRaiseHand";
internal const string AudioMuted = "zoomAudioMuted";
internal const string Enabled = "zoomEnable";
internal const string MeetingNumber = "zoomRoom";
internal const string ReceiveAudio = "zoomReceiveAudio";
internal const string ReceiveVideo = "zoomReceiveVideo";
internal const string SendAudio = "zoomSendAudio";
internal const string SendVideo = "zoomSendVideo";
internal const string Signature = "zoomToken";
internal const string Subject = "zoomSubject";
internal const string UserName = "zoomUserInfoDisplayName";
internal const string VideoMuted = "zoomVideoMuted";
}
}
internal static class Security

View file

@ -242,7 +242,6 @@ namespace SafeExamBrowser.Monitoring.Applications
{
var isClient = true;
var isRuntime = true;
var isWebView = true;
isClient &= process.Name == "SafeExamBrowser.Client.exe";
isClient &= process.OriginalName == "SafeExamBrowser.Client.exe";
@ -250,16 +249,12 @@ namespace SafeExamBrowser.Monitoring.Applications
isRuntime &= process.Name == "SafeExamBrowser.exe";
isRuntime &= process.OriginalName == "SafeExamBrowser.exe";
isWebView &= process.Name == "msedgewebview2.exe";
isWebView &= process.OriginalName == "msedgewebview2.exe";
#if !DEBUG
isClient &= process.Signature == "2bc82fe8e56a39f96bc6c4b91d6703a0379b76a2";
isRuntime &= process.Signature == "2bc82fe8e56a39f96bc6c4b91d6703a0379b76a2";
isWebView &= process.Signature == "a4baabd12432ab9c7c297385260e95c3dae83bf2";
#endif
return isClient || isRuntime || isWebView;
return isClient || isRuntime;
}
private void Close(Window window)

View file

@ -1,255 +0,0 @@
/*
* 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.IO;
using System.Reflection;
using System.Threading;
using System.Windows;
using Microsoft.Web.WebView2.Wpf;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts.Events.Proctoring;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
namespace SafeExamBrowser.Proctoring.JitsiMeet
{
internal class JitsiMeetImplementation : ProctoringImplementation
{
private readonly AppConfig appConfig;
private readonly IFileSystem fileSystem;
private readonly IModuleLogger logger;
private readonly ProctoringSettings settings;
private readonly IText text;
private readonly IUserInterfaceFactory uiFactory;
private ProctoringControl control;
private string filePath;
private WindowVisibility initialVisibility;
private IProctoringWindow window;
internal override string Name => nameof(JitsiMeet);
internal JitsiMeetImplementation(
AppConfig appConfig,
IFileSystem fileSystem,
IModuleLogger logger,
ProctoringSettings settings,
IText text,
IUserInterfaceFactory uiFactory)
{
this.appConfig = appConfig;
this.fileSystem = fileSystem;
this.logger = logger;
this.settings = settings;
this.text = text;
this.uiFactory = uiFactory;
}
internal override void Initialize()
{
var start = true;
initialVisibility = settings.WindowVisibility;
settings.JitsiMeet.ServerUrl = Sanitize(settings.JitsiMeet.ServerUrl);
start &= !string.IsNullOrWhiteSpace(settings.JitsiMeet.RoomName);
start &= !string.IsNullOrWhiteSpace(settings.JitsiMeet.ServerUrl);
if (start)
{
logger.Info($"Initialized proctoring: All settings are valid, starting automatically...");
Start();
}
else
{
ShowNotificationInactive();
logger.Info($"Initialized proctoring: Not all settings are valid or a server session is active, not starting automatically.");
}
}
internal override void ProctoringConfigurationReceived(bool allowChat, bool receiveAudio, bool receiveVideo)
{
logger.Info("Proctoring configuration received.");
settings.JitsiMeet.AllowChat = allowChat;
settings.JitsiMeet.ReceiveAudio = receiveAudio;
settings.JitsiMeet.ReceiveVideo = receiveVideo;
if (allowChat || receiveVideo)
{
settings.WindowVisibility = WindowVisibility.AllowToHide;
}
else
{
settings.WindowVisibility = initialVisibility;
}
Stop();
Start();
logger.Info($"Successfully updated configuration: {nameof(allowChat)}={allowChat}, {nameof(receiveAudio)}={receiveAudio}, {nameof(receiveVideo)}={receiveVideo}.");
}
internal override void ProctoringInstructionReceived(InstructionEventArgs args)
{
if (args is JitsiMeetInstruction instruction)
{
logger.Info($"Proctoring instruction received: {instruction.Method}");
if (instruction.Method == InstructionMethod.Join)
{
settings.JitsiMeet.RoomName = instruction.RoomName;
settings.JitsiMeet.ServerUrl = instruction.ServerUrl;
settings.JitsiMeet.Token = instruction.Token;
Stop();
Start();
}
else
{
Stop();
}
logger.Info("Successfully processed instruction.");
}
}
internal override void Start()
{
Application.Current.Dispatcher.Invoke(() =>
{
try
{
var content = LoadContent(settings);
filePath = Path.Combine(appConfig.TemporaryDirectory, $"{Path.GetRandomFileName()}_index.html");
fileSystem.Save(content, filePath);
control = new ProctoringControl(logger.CloneFor(nameof(ProctoringControl)), settings);
control.CreationProperties = new CoreWebView2CreationProperties { UserDataFolder = appConfig.TemporaryDirectory };
control.EnsureCoreWebView2Async().ContinueWith(_ =>
{
control.Dispatcher.Invoke(() =>
{
control.CoreWebView2.Navigate(filePath);
});
});
window = uiFactory.CreateProctoringWindow(control);
window.SetTitle(settings.JitsiMeet.Enabled ? settings.JitsiMeet.Subject : "");
window.Show();
if (settings.WindowVisibility == WindowVisibility.AllowToShow || settings.WindowVisibility == WindowVisibility.Hidden)
{
window.Hide();
}
ShowNotificationActive();
logger.Info("Started proctoring.");
}
catch (Exception e)
{
logger.Error($"Failed to start proctoring! Reason: {e.Message}", e);
}
});
}
internal override void Stop()
{
if (control != default && window != default)
{
control.Dispatcher.Invoke(() =>
{
control.ExecuteScriptAsync("api.executeCommand('hangup'); api.dispose();");
Thread.Sleep(2000);
window.Close();
control = default;
window = default;
fileSystem.Delete(filePath);
ShowNotificationInactive();
logger.Info("Stopped proctoring.");
});
}
}
internal override void Terminate()
{
Stop();
logger.Info("Terminated proctoring.");
}
protected override void ActivateNotification()
{
if (settings.WindowVisibility == WindowVisibility.Visible)
{
window?.BringToForeground();
}
else if (settings.WindowVisibility == WindowVisibility.AllowToHide || settings.WindowVisibility == WindowVisibility.AllowToShow)
{
window?.Toggle();
}
}
private string LoadContent(ProctoringSettings settings)
{
var assembly = Assembly.GetAssembly(typeof(ProctoringController));
var path = $"{typeof(ProctoringController).Namespace}.JitsiMeet.index.html";
using (var stream = assembly.GetManifestResourceStream(path))
using (var reader = new StreamReader(stream))
{
var html = reader.ReadToEnd();
html = html.Replace("%%_ALLOW_CHAT_%%", settings.JitsiMeet.AllowChat ? "chat" : "");
html = html.Replace("%%_ALLOW_CLOSED_CAPTIONS_%%", settings.JitsiMeet.AllowClosedCaptions ? "closedcaptions" : "");
html = html.Replace("%%_ALLOW_RAISE_HAND_%%", settings.JitsiMeet.AllowRaiseHand ? "raisehand" : "");
html = html.Replace("%%_ALLOW_RECORDING_%%", settings.JitsiMeet.AllowRecording ? "recording" : "");
html = html.Replace("%%_ALLOW_TILE_VIEW", settings.JitsiMeet.AllowTileView ? "tileview" : "");
html = html.Replace("'%_AUDIO_MUTED_%'", settings.JitsiMeet.AudioMuted && settings.WindowVisibility != WindowVisibility.Hidden ? "true" : "false");
html = html.Replace("'%_AUDIO_ONLY_%'", settings.JitsiMeet.AudioOnly ? "true" : "false");
html = html.Replace("'%_VIDEO_MUTED_%'", settings.JitsiMeet.VideoMuted && settings.WindowVisibility != WindowVisibility.Hidden ? "true" : "false");
return html;
}
}
private string Sanitize(string serverUrl)
{
return serverUrl?.Replace($"{Uri.UriSchemeHttp}{Uri.SchemeDelimiter}", "").Replace($"{Uri.UriSchemeHttps}{Uri.SchemeDelimiter}", "");
}
private void ShowNotificationActive()
{
CanActivate = true;
IconResource = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Proctoring_Active.xaml") };
Tooltip = text.Get(TextKey.Notification_ProctoringActiveTooltip);
InvokeNotificationChanged();
}
private void ShowNotificationInactive()
{
CanActivate = false;
IconResource = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Proctoring_Inactive.xaml") };
Tooltip = text.Get(TextKey.Notification_ProctoringInactiveTooltip);
InvokeNotificationChanged();
}
}
}

View file

@ -1,69 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
</head>
<body style="margin: 0">
<div id="placeholder" />
<script src='https://meet.jit.si/external_api.js'></script>
<script type="text/javascript">
var api;
function startMeeting(credentials) {
var configOverwrite = {
disableProfile: true,
prejoinPageEnabled: false,
startAudioOnly: '%_AUDIO_ONLY_%',
startWithAudioMuted: '%_AUDIO_MUTED_%',
startWithVideoMuted: '%_VIDEO_MUTED_%'
};
var interfaceOverwrite = {
JITSI_WATERMARK_LINK: '',
SHOW_JITSI_WATERMARK: false,
TOOLBAR_BUTTONS: [
'microphone', 'camera', '%%_ALLOW_CLOSED_CAPTIONS_%%', /*'desktop',*/ 'embedmeeting', 'fullscreen',
'fodeviceselection', /*'hangup',*/ 'profile', '%%_ALLOW_CHAT_%%', '%%_ALLOW_RECORDING_%%',
'livestreaming', 'etherpad', /*'sharedvideo',*/ 'settings', '%%_ALLOW_RAISE_HAND_%%',
'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
'%%_ALLOW_TILE_VIEW_%%', 'select-background', 'download', 'help', /*'mute-everyone',*/ 'mute-video-everyone', 'security'
]
};
var options = {
configOverwrite: configOverwrite,
height: '100%',
interfaceConfigOverwrite: interfaceOverwrite,
jwt: credentials.token,
parentNode: document.querySelector('#placeholder'),
roomName: credentials.roomName,
width: '100%'
};
api = new JitsiMeetExternalAPI(credentials.domain, options);
api.addListener('audioMuteStatusChanged', args => {
if (args.muted) {
api.executeCommand('toggleAudio');
}
});
api.addListener('videoMuteStatusChanged', args => {
if (args.muted) {
api.executeCommand('toggleVideo');
}
});
api.executeCommand('subject', credentials.subject);
}
function webMessageReceived(args) {
if ('credentials' in args.data) {
startMeeting(args.data.credentials);
}
}
window.addEventListener('unload', () => {
api.executeCommand('hangup');
api.dispose();
});
window.chrome.webview.addEventListener('message', webMessageReceived);
window.chrome.webview.postMessage('credentials');
</script>
</body>
</html>

View file

@ -1,104 +0,0 @@
/*
* 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 Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
using Newtonsoft.Json.Linq;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
namespace SafeExamBrowser.Proctoring
{
internal class ProctoringControl : WebView2, IProctoringControl
{
private readonly ILogger logger;
private readonly ProctoringSettings settings;
public event FullScreenChangedEventHandler FullScreenChanged;
internal ProctoringControl(ILogger logger, ProctoringSettings settings)
{
this.logger = logger;
this.settings = settings;
CoreWebView2InitializationCompleted += ProctoringControl_CoreWebView2InitializationCompleted;
}
private void ProctoringControl_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
{
if (e.IsSuccess)
{
CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
CoreWebView2.Settings.AreDevToolsEnabled = false;
CoreWebView2.Settings.IsStatusBarEnabled = false;
CoreWebView2.ContainsFullScreenElementChanged += CoreWebView2_ContainsFullScreenElementChanged;
CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested;
CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;
logger.Info("Successfully initialized.");
}
else
{
logger.Error("Failed to initialize!", e.InitializationException);
}
}
private void CoreWebView2_ContainsFullScreenElementChanged(object sender, object e)
{
FullScreenChanged?.Invoke(CoreWebView2.ContainsFullScreenElement);
logger.Debug($"Full screen {(CoreWebView2.ContainsFullScreenElement ? "activated" : "deactivated")}.");
}
private void CoreWebView2_PermissionRequested(object sender, CoreWebView2PermissionRequestedEventArgs e)
{
if (e.PermissionKind == CoreWebView2PermissionKind.Camera || e.PermissionKind == CoreWebView2PermissionKind.Microphone)
{
e.State = CoreWebView2PermissionState.Allow;
logger.Info($"Granted access to {e.PermissionKind}.");
}
else
{
logger.Info($"Denied access to {e.PermissionKind}.");
}
}
private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
{
var message = e.TryGetWebMessageAsString();
logger.Debug($"Received web message '{message}'.");
switch (message)
{
case "credentials":
SendCredentials();
break;
}
}
private void SendCredentials()
{
var message = new JObject();
var credentials = new JObject();
if (settings.JitsiMeet.Enabled)
{
credentials.Add(new JProperty("domain", settings.JitsiMeet.ServerUrl));
credentials.Add(new JProperty("roomName", settings.JitsiMeet.RoomName));
credentials.Add(new JProperty("subject", settings.JitsiMeet.ShowMeetingName ? settings.JitsiMeet.Subject : ""));
credentials.Add(new JProperty("token", settings.JitsiMeet.Token));
}
message.Add("credentials", credentials);
logger.Debug("Sending credentials to proctoring client.");
CoreWebView2.PostWebMessageAsJson(message.ToString());
}
}
}

View file

@ -12,7 +12,6 @@ using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Proctoring.JitsiMeet;
using SafeExamBrowser.Proctoring.ScreenProctoring;
using SafeExamBrowser.Proctoring.ScreenProctoring.Service;
using SafeExamBrowser.Settings.Proctoring;
@ -57,13 +56,6 @@ namespace SafeExamBrowser.Proctoring
{
var implementations = new List<ProctoringImplementation>();
if (settings.JitsiMeet.Enabled)
{
var logger = this.logger.CloneFor(nameof(JitsiMeet));
implementations.Add(new JitsiMeetImplementation(appConfig, fileSystem, logger, settings, text, uiFactory));
}
if (settings.ScreenProctoring.Enabled)
{
var logger = this.logger.CloneFor(nameof(ScreenProctoring));

View file

@ -62,15 +62,6 @@
<Reference Include="KGySoft.Drawing.Core, Version=8.1.0.0, Culture=neutral, PublicKeyToken=b45eba277439ddfe, processorArchitecture=MSIL">
<HintPath>..\packages\KGySoft.Drawing.Core.8.1.0\lib\net46\KGySoft.Drawing.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.WebView2.Core, Version=1.0.2365.46, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.2365.46\lib\net45\Microsoft.Web.WebView2.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.WebView2.WinForms, Version=1.0.2365.46, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.2365.46\lib\net45\Microsoft.Web.WebView2.WinForms.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.WebView2.Wpf, Version=1.0.2365.46, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.2365.46\lib\net45\Microsoft.Web.WebView2.Wpf.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
@ -87,8 +78,6 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="JitsiMeet\JitsiMeetImplementation.cs" />
<Compile Include="ProctoringControl.cs" />
<Compile Include="ProctoringController.cs" />
<Compile Include="ProctoringFactory.cs" />
<Compile Include="ProctoringImplementation.cs" />
@ -176,19 +165,9 @@
<Name>SafeExamBrowser.WindowsApi.Contracts</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="JitsiMeet\index.html" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Microsoft.Web.WebView2.1.0.2365.46\build\Microsoft.Web.WebView2.targets" Condition="Exists('..\packages\Microsoft.Web.WebView2.1.0.2365.46\build\Microsoft.Web.WebView2.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Web.WebView2.1.0.2365.46\build\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Web.WebView2.1.0.2365.46\build\Microsoft.Web.WebView2.targets'))" />
</Target>
</Project>

View file

@ -3,6 +3,5 @@
<package id="KGySoft.CoreLibraries" version="8.1.0" targetFramework="net48" />
<package id="KGySoft.Drawing" version="8.1.0" targetFramework="net48" />
<package id="KGySoft.Drawing.Core" version="8.1.0" targetFramework="net48" />
<package id="Microsoft.Web.WebView2" version="1.0.2365.46" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
</packages>

View file

@ -44,7 +44,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
var count = 0;
settings.Proctoring.JitsiMeet.Enabled = true;
settings.Proctoring.ScreenProctoring.Enabled = true;
sut.ActionRequired += (args) =>
@ -58,7 +57,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
var result = sut.Perform();
Assert.AreEqual(2, count);
Assert.AreEqual(1, count);
Assert.AreEqual(OperationResult.Success, result);
}
@ -67,8 +66,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
var disclaimerShown = false;
settings.Proctoring.JitsiMeet.Enabled = true;
settings.Proctoring.ScreenProctoring.Enabled = false;
settings.Proctoring.ScreenProctoring.Enabled = true;
sut.ActionRequired += (args) =>
{
@ -83,15 +81,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
Assert.IsTrue(disclaimerShown);
Assert.AreEqual(OperationResult.Aborted, result);
disclaimerShown = false;
settings.Proctoring.JitsiMeet.Enabled = false;
settings.Proctoring.ScreenProctoring.Enabled = true;
result = sut.Repeat();
Assert.IsTrue(disclaimerShown);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
@ -99,7 +88,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
var disclaimerShown = false;
settings.Proctoring.Enabled = false;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)
@ -120,7 +108,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
var count = 0;
settings.Proctoring.JitsiMeet.Enabled = true;
settings.Proctoring.ScreenProctoring.Enabled = true;
sut.ActionRequired += (args) =>
@ -134,7 +121,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
var result = sut.Perform();
Assert.AreEqual(2, count);
Assert.AreEqual(1, count);
Assert.AreEqual(OperationResult.Success, result);
}
@ -143,8 +130,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
var disclaimerShown = false;
settings.Proctoring.JitsiMeet.Enabled = true;
settings.Proctoring.ScreenProctoring.Enabled = false;
settings.Proctoring.ScreenProctoring.Enabled = true;
sut.ActionRequired += (args) =>
{
@ -159,15 +145,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
Assert.IsTrue(disclaimerShown);
Assert.AreEqual(OperationResult.Aborted, result);
disclaimerShown = false;
settings.Proctoring.JitsiMeet.Enabled = false;
settings.Proctoring.ScreenProctoring.Enabled = true;
result = sut.Repeat();
Assert.IsTrue(disclaimerShown);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
@ -175,7 +152,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
var disclaimerShown = false;
settings.Proctoring.Enabled = false;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)
@ -196,7 +172,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
var disclaimerShown = false;
settings.Proctoring.Enabled = true;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)

View file

@ -1,100 +0,0 @@
/*
* 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Security;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class ProctoringWorkaroundOperationTests
{
private SessionContext context;
private Mock<ILogger> logger;
private AppSettings settings;
private ProctoringWorkaroundOperation sut;
[TestInitialize]
public void Initialize()
{
context = new SessionContext();
logger = new Mock<ILogger>();
settings = new AppSettings();
context.Next = new SessionConfiguration();
context.Next.Settings = settings;
sut = new ProctoringWorkaroundOperation(logger.Object, context);
}
[TestMethod]
public void Perform_MustSwitchToDisableExplorerShellIfProctoringActive()
{
settings.Proctoring.JitsiMeet.Enabled = true;
settings.Security.KioskMode = KioskMode.CreateNewDesktop;
var result = sut.Perform();
Assert.AreEqual(KioskMode.DisableExplorerShell, settings.Security.KioskMode);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustDoNothingIfProctoringNotActive()
{
settings.Proctoring.JitsiMeet.Enabled = false;
settings.Security.KioskMode = KioskMode.None;
var result = sut.Perform();
Assert.AreEqual(KioskMode.None, settings.Security.KioskMode);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustSwitchToDisableExplorerShellIfProctoringActive()
{
settings.Proctoring.JitsiMeet.Enabled = true;
settings.Security.KioskMode = KioskMode.CreateNewDesktop;
var result = sut.Repeat();
Assert.AreEqual(KioskMode.DisableExplorerShell, settings.Security.KioskMode);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustDoNothingIfProctoringNotActive()
{
settings.Proctoring.JitsiMeet.Enabled = false;
settings.Security.KioskMode = KioskMode.None;
var result = sut.Repeat();
Assert.AreEqual(KioskMode.None, settings.Security.KioskMode);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustDoNothing()
{
settings.Proctoring.JitsiMeet.Enabled = true;
settings.Security.KioskMode = KioskMode.None;
var result = sut.Revert();
Assert.AreEqual(KioskMode.None, settings.Security.KioskMode);
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View file

@ -145,7 +145,6 @@
<Compile Include="Operations\DisclaimerOperationTests.cs" />
<Compile Include="Operations\DisplayMonitorOperationTests.cs" />
<Compile Include="Operations\KioskModeOperationTests.cs" />
<Compile Include="Operations\ProctoringWorkaroundOperationTests.cs" />
<Compile Include="Operations\RemoteSessionOperationTests.cs" />
<Compile Include="Operations\ServerOperationTests.cs" />
<Compile Include="Operations\ServiceOperationTests.cs" />

View file

@ -106,7 +106,6 @@ namespace SafeExamBrowser.Runtime
sessionOperations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, sessionContext, text));
sessionOperations.Enqueue(new ServiceOperation(logger, runtimeHost, serviceProxy, sessionContext, THIRTY_SECONDS, userInfo));
sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS));
sessionOperations.Enqueue(new ProctoringWorkaroundOperation(logger, sessionContext));
sessionOperations.Enqueue(new KioskModeOperation(desktopFactory, desktopMonitor, explorerShell, logger, processFactory, sessionContext));
sessionOperations.Enqueue(new ClientOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS));
sessionOperations.Enqueue(new SessionActivationOperation(logger, sessionContext));

View file

@ -31,21 +31,11 @@ namespace SafeExamBrowser.Runtime.Operations
{
var result = OperationResult.Success;
if (Context.Next.Settings.Proctoring.JitsiMeet.Enabled)
{
result = ShowVideoProctoringDisclaimer();
}
if (result == OperationResult.Success && Context.Next.Settings.Proctoring.ScreenProctoring.Enabled)
if (Context.Next.Settings.Proctoring.ScreenProctoring.Enabled)
{
result = ShowScreenProctoringDisclaimer();
}
if (result == OperationResult.Success && Context.Next.Settings.Proctoring.Zoom.Enabled)
{
result = ShowZoomError();
}
return result;
}
@ -53,21 +43,11 @@ namespace SafeExamBrowser.Runtime.Operations
{
var result = OperationResult.Success;
if (Context.Next.Settings.Proctoring.JitsiMeet.Enabled)
{
result = ShowVideoProctoringDisclaimer();
}
if (result == OperationResult.Success && Context.Next.Settings.Proctoring.ScreenProctoring.Enabled)
if (Context.Next.Settings.Proctoring.ScreenProctoring.Enabled)
{
result = ShowScreenProctoringDisclaimer();
}
if (result == OperationResult.Success && Context.Next.Settings.Proctoring.Zoom.Enabled)
{
result = ShowZoomError();
}
return result;
}
@ -102,50 +82,5 @@ namespace SafeExamBrowser.Runtime.Operations
return OperationResult.Aborted;
}
}
private OperationResult ShowVideoProctoringDisclaimer()
{
var args = new MessageEventArgs
{
Action = MessageBoxAction.OkCancel,
Icon = MessageBoxIcon.Information,
Message = TextKey.MessageBox_VideoProctoringDisclaimer,
Title = TextKey.MessageBox_VideoProctoringDisclaimerTitle
};
StatusChanged?.Invoke(TextKey.OperationStatus_WaitDisclaimerConfirmation);
ActionRequired?.Invoke(args);
if (args.Result == MessageBoxResult.Ok)
{
logger.Info("The user confirmed the video proctoring disclaimer.");
return OperationResult.Success;
}
else
{
logger.Warn("The user did not confirm the video proctoring disclaimer! Aborting session initialization...");
return OperationResult.Aborted;
}
}
private OperationResult ShowZoomError()
{
var args = new MessageEventArgs
{
Action = MessageBoxAction.Ok,
Icon = MessageBoxIcon.Error,
Message = TextKey.MessageBox_ZoomNotSupported,
Title = TextKey.MessageBox_ZoomNotSupportedTitle
};
logger.Error("Zoom proctoring is enabled but not supported! Aborting session initialization...");
StatusChanged?.Invoke(TextKey.OperationStatus_WaitErrorConfirmation);
ActionRequired?.Invoke(args);
return OperationResult.Aborted;
}
}
}

View file

@ -1,49 +0,0 @@
/*
* 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 SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings.Security;
namespace SafeExamBrowser.Runtime.Operations
{
internal class ProctoringWorkaroundOperation : SessionOperation
{
private readonly ILogger logger;
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public override event StatusChangedEventHandler StatusChanged { add { } remove { } }
public ProctoringWorkaroundOperation(ILogger logger, SessionContext context) : base(context)
{
this.logger = logger;
}
public override OperationResult Perform()
{
if (Context.Next.Settings.Proctoring.JitsiMeet.Enabled && Context.Next.Settings.Security.KioskMode == KioskMode.CreateNewDesktop)
{
Context.Next.Settings.Security.KioskMode = KioskMode.DisableExplorerShell;
logger.Info("Switched kiosk mode to Disable Explorer Shell due to remote proctoring being enabled.");
}
return OperationResult.Success;
}
public override OperationResult Repeat()
{
return Perform();
}
public override OperationResult Revert()
{
return OperationResult.Success;
}
}
}

View file

@ -109,7 +109,6 @@
<Compile Include="Operations\ApplicationIntegrityOperation.cs" />
<Compile Include="Operations\Events\VersionRestrictionMessageArgs.cs" />
<Compile Include="Operations\KioskModeOperation.cs" />
<Compile Include="Operations\ProctoringWorkaroundOperation.cs" />
<Compile Include="Operations\RemoteSessionOperation.cs" />
<Compile Include="Operations\ServerOperation.cs" />
<Compile Include="Operations\ServiceOperation.cs" />

View file

@ -1,109 +0,0 @@
/*
* 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;
namespace SafeExamBrowser.Settings.Proctoring
{
/// <summary>
/// All settings for the meeting provider Jitsi Meet.
/// </summary>
[Serializable]
public class JitsiMeetSettings
{
/// <summary>
/// Determines whether the user can use the chat.
/// </summary>
public bool AllowChat { get; set; }
/// <summary>
/// Determines whether the user can use close captions.
/// </summary>
public bool AllowClosedCaptions { get; set; }
/// <summary>
/// Determines whether the user can use the raise hand feature.
/// </summary>
public bool AllowRaiseHand { get; set; }
/// <summary>
/// Determines whether the user can record the meeting.
/// </summary>
public bool AllowRecording { get; set; }
/// <summary>
/// Determines whether the user may use the tile view.
/// </summary>
public bool AllowTileView { get; set; }
/// <summary>
/// Determines whether the audio starts muted.
/// </summary>
public bool AudioMuted { get; set; }
/// <summary>
/// Determines whether the meeting runs in an audio-only mode.
/// </summary>
public bool AudioOnly { get; set; }
/// <summary>
/// Determines whether proctoring with Jitsi Meet is enabled.
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// Determines whether the user may receive the video stream of other meeting participants.
/// </summary>
public bool ReceiveAudio { get; set; }
/// <summary>
/// Determines whether the user may receive the audio stream of other meeting participants.
/// </summary>
public bool ReceiveVideo { get; set; }
/// <summary>
/// The name of the meeting room.
/// </summary>
public string RoomName { get; set; }
/// <summary>
/// Determines whether the audio stream of the user will be sent to the server.
/// </summary>
public bool SendAudio { get; set; }
/// <summary>
/// Determines whether the video stream of the user will be sent to the server.
/// </summary>
public bool SendVideo { get; set; }
/// <summary>
/// The URL of the Jitsi Meet server.
/// </summary>
public string ServerUrl { get; set; }
/// <summary>
/// Determines whether the subject will be shown as meeting name.
/// </summary>
public bool ShowMeetingName { get; set; }
/// <summary>
/// The subject of the meeting.
/// </summary>
public string Subject { get; set; }
/// <summary>
/// The authentication token for the meeting.
/// </summary>
public string Token { get; set; }
/// <summary>
/// Determines whether the video starts muted.
/// </summary>
public bool VideoMuted { get; set; }
}
}

View file

@ -26,11 +26,6 @@ namespace SafeExamBrowser.Settings.Proctoring
/// </summary>
public bool ForceRaiseHandMessage { get; set; }
/// <summary>
/// All settings for remote proctoring with Jitsi Meet.
/// </summary>
public JitsiMeetSettings JitsiMeet { get; set; }
/// <summary>
/// All settings for the screen proctoring.
/// </summary>
@ -46,21 +41,9 @@ namespace SafeExamBrowser.Settings.Proctoring
/// </summary>
public bool ShowTaskbarNotification { get; set; }
/// <summary>
/// Determines the visibility of the proctoring window.
/// </summary>
public WindowVisibility WindowVisibility { get; set; }
/// <summary>
/// All settings for remote proctoring with Zoom.
/// </summary>
public ZoomSettings Zoom { get; set; }
public ProctoringSettings()
{
JitsiMeet = new JitsiMeetSettings();
ScreenProctoring = new ScreenProctoringSettings();
Zoom = new ZoomSettings();
}
}
}

View file

@ -1,36 +0,0 @@
/*
* 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.Settings.Proctoring
{
/// <summary>
/// Defines all possible visibility states for the proctoring window.
/// </summary>
public enum WindowVisibility
{
/// <summary>
/// The proctoring window is hidden and cannot be shown by the user.
/// </summary>
Hidden,
/// <summary>
/// The proctoring window is initially hidden but may be shown by the user.
/// </summary>
AllowToShow,
/// <summary>
/// The proctoring window is initially visible but may be hidden by the user.
/// </summary>
AllowToHide,
/// <summary>
/// The proctoring window is always visible and cannot be hidden by the user.
/// </summary>
Visible
}
}

View file

@ -1,99 +0,0 @@
/*
* 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;
namespace SafeExamBrowser.Settings.Proctoring
{
/// <summary>
/// All settings for the meeting provider Zoom.
/// </summary>
[Serializable]
public class ZoomSettings
{
/// <summary>
/// Determines whether the user can use the chat.
/// </summary>
public bool AllowChat { get; set; }
/// <summary>
/// Determines whether the user can use closed captions.
/// </summary>
public bool AllowClosedCaptions { get; set; }
/// <summary>
/// Determines whether the user can use the raise hand feature.
/// </summary>
public bool AllowRaiseHand { get; set; }
/// <summary>
/// Determines whether the audio starts muted.
/// </summary>
public bool AudioMuted { get; set; }
/// <summary>
/// Determines whether proctoring with Zoom is enabled.
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// The number of the meeting.
/// </summary>
public string MeetingNumber { get; set; }
/// <summary>
/// The password of the meeting.
/// </summary>
public string Password { get; set; }
/// <summary>
/// Determines whether the user may receive the video stream of other meeting participants.
/// </summary>
public bool ReceiveAudio { get; set; }
/// <summary>
/// Determines whether the user may receive the audio stream of other meeting participants.
/// </summary>
public bool ReceiveVideo { get; set; }
/// <summary>
/// The SDK key to be used for authentication.
/// </summary>
public string SdkKey { get; set; }
/// <summary>
/// Determines whether the audio stream of the user will be sent to the server.
/// </summary>
public bool SendAudio { get; set; }
/// <summary>
/// Determines whether the video stream of the user will be sent to the server.
/// </summary>
public bool SendVideo { get; set; }
/// <summary>
/// The signature to be used for authentication.
/// </summary>
public string Signature { get; set; }
/// <summary>
/// The subject of the meeting.
/// </summary>
public string Subject { get; set; }
/// <summary>
/// The user name to be used for the meeting.
/// </summary>
public string UserName { get; set; }
/// <summary>
/// Determines whether the video starts muted.
/// </summary>
public bool VideoMuted { get; set; }
}
}

View file

@ -75,12 +75,9 @@
<Compile Include="Monitoring\DisplaySettings.cs" />
<Compile Include="Proctoring\ImageFormat.cs" />
<Compile Include="Proctoring\ImageQuantization.cs" />
<Compile Include="Proctoring\JitsiMeetSettings.cs" />
<Compile Include="Proctoring\MetaDataSettings.cs" />
<Compile Include="Proctoring\ProctoringSettings.cs" />
<Compile Include="Proctoring\ScreenProctoringSettings.cs" />
<Compile Include="Proctoring\WindowVisibility.cs" />
<Compile Include="Proctoring\ZoomSettings.cs" />
<Compile Include="Security\ClipboardPolicy.cs" />
<Compile Include="Security\VersionRestriction.cs" />
<Compile Include="SessionMode.cs" />

View file

@ -43,11 +43,6 @@
Copyright © 2008-2024 The Chromium Embedded Framework Authors. All rights reserved.
<LineBreak />
<LineBreak />
<Bold><Underline>Edge WebView2 Runtime</Underline></Bold>
<LineBreak />
Copyright © 2019-2024 Microsoft. All rights reserved.
<LineBreak />
<LineBreak />
<Bold><Underline>Font-Awesome-WPF</Underline></Bold>
<LineBreak />
Copyright © 2014-2024 charri. All rights reserved.

View file

@ -43,11 +43,6 @@
Copyright © 2008-2024 The Chromium Embedded Framework Authors. All rights reserved.
<LineBreak />
<LineBreak />
<Bold><Underline>Edge WebView2 Runtime</Underline></Bold>
<LineBreak />
Copyright © 2019-2024 Microsoft. All rights reserved.
<LineBreak />
<LineBreak />
<Bold><Underline>Font-Awesome-WPF</Underline></Bold>
<LineBreak />
Copyright © 2014-2024 charri. All rights reserved.

View file

@ -3,15 +3,15 @@
{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f47\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
{\f48\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f50\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f51\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f52\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\f53\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f54\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f55\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f57\fbidi \fswiss\fcharset238\fprq2 Arial CE;}
{\f58\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f60\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\f61\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f62\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}
{\f63\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f64\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}{\f65\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f387\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}
{\f388\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f390\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f391\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f394\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}
{\f395\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\f417\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f418\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f420\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}
{\f421\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f422\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\f423\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\f424\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}
{\f425\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f46\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
{\f47\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f49\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f50\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f51\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\f52\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f53\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f54\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f56\fbidi \fswiss\fcharset238\fprq2 Arial CE;}
{\f57\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f59\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\f60\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f61\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}
{\f62\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f63\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}{\f64\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f386\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}
{\f387\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f389\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f390\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f393\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}
{\f394\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\f416\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f417\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f419\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}
{\f420\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f421\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\f422\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\f423\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}
{\f424\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
@ -41,8 +41,8 @@
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang2055\langfe2052\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2055\langfenp2052 \snext11 \ssemihidden \sunhideused
Normal Table;}}{\*\listtable{\list\listtemplateid-1\listsimple{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat0\levelspace0\levelindent0{\leveltext\'01*;}{\levelnumbers;}}{\listname ;}\listid-2}}{\*\listoverridetable
{\listoverride\listid-2\listoverridecount1{\lfolevel\listoverrideformat{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat0\levelold\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 }}\ls1}}
{\*\rsidtbl \rsid1002736\rsid1848078\rsid11218156\rsid13781912}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator B\'fcchel Damian (ID)}
{\creatim\yr2023\mo3\dy8\min15}{\revtim\yr2024\mo3\dy5\hr18\min16}{\version4}{\edmins0}{\nofpages2}{\nofwords340}{\nofchars2143}{\nofcharsws2479}{\vern89}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}
{\*\rsidtbl \rsid1002736\rsid1848078\rsid11218156\rsid13781912\rsid15287357}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator B\'fcchel Damian (ID)}
{\creatim\yr2023\mo3\dy8\min15}{\revtim\yr2024\mo4\dy16\hr18\min43}{\version5}{\edmins0}{\nofpages1}{\nofwords306}{\nofchars1935}{\nofcharsws2237}{\vern93}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}
\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect
\widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120
\dghorigin1701\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot1002736 \nouicompat \fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1
@ -57,13 +57,13 @@ Safe Exam Browser for Windows}{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\
\par }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1
This application is subject to the terms of the Mozilla Public License, version 2.0. If a copy of the MPL was not distributed with this application, you can obtain one at }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0
\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://mozilla.org/MPL/2.0/ }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f006d006f007a0069006c006c0061002e006f00720067002f004d0050004c002f0032002e0030002f000000795881f43b1d7f48af2c825dc485276300000000a5ab000300}}
}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 http://mozilla.org/MPL/2.0/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f006d006f007a0069006c006c0061002e006f00720067002f004d0050004c002f0032002e0030002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00030000}
}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 http://mozilla.org/MPL/2.0/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20
\ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 .
\par \hich\af1\dbch\af31505\loch\f1 See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 HYPERLINK https://github.com/SafeExamBrowser }{\rtlch\fcs1
\af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5e000000680074007400700073003a002f002f006700690074006800750062002e0063006f006d002f0053006100660065004500780061006d00420072006f0077007300650072000000795881f43b1d7f48af2c825d
c485276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://github.com/SafeExamBrowser}}}\sectd \ltrsect
c485276300000000a5ab00030054}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://github.com/SafeExamBrowser}}}\sectd \ltrsect
\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 for more information about Safe Exam Browser.
\par
\par }{\rtlch\fcs1 \ab\af1\afs24 \ltrch\fcs0 \b\f1\fs24\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 Frameworks & Third-Party Software}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0
@ -73,11 +73,11 @@ c485276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\f
\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3 {\pntxtb \'b7}}\faauto\ls1\rin0\lin720\itap0\pararsid1002736 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
\hich\af1\dbch\af31505\loch\f1 .NET Framework\line }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 \hich\f1 Copyright \'a9\loch\f1 2002-}{\rtlch\fcs1 \af1\afs20
\ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid1002736\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 202}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid1848078 \hich\af1\dbch\af31505\loch\f1 4}{\rtlch\fcs1
\af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 Microsoft. All rights reserved. }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid13781912
\hich\af1\dbch\af31505\loch\f1 See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid13781912 \hich\af1\dbch\af31505\loch\f1 HYPERLINK https://dotnet.microsoft.com/ }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0
\f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
\af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 Micros\hich\af1\dbch\af31505\loch\f1 oft. All rights reserved. }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0
\f1\fs20\lang7\langfe2052\langnp7\insrsid13781912 \hich\af1\dbch\af31505\loch\f1 See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid13781912 \hich\af1\dbch\af31505\loch\f1
HYPERLINK https://dotnet.microsoft.com/ }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b54000000680074007400700073003a002f002f0064006f0074006e00650074002e006d006900630072006f0073006f00660074002e0063006f006d002f000000795881f43b1d7f48af2c825dc485276300000000a5ab
000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid13781912 \hich\af1\dbch\af31505\loch\f1 https://dotnet.microsoft.com/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0
00030000}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid13781912 \hich\af1\dbch\af31505\loch\f1 https://dotnet.microsoft.com/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0
\f1\fs20\lang7\langfe2052\langnp7\insrsid13781912 \hich\af1\dbch\af31505\loch\f1 for more information.}{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang7\langfe2052\langnp7\insrsid13781912
\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \f3\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1
\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3 {\pntxtb \'b7}}\faauto\ls1\rin0\lin720\itap0\pararsid1002736 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
@ -87,7 +87,7 @@ Copyright \'a9\loch\f1 2010-}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1
\af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 HYPERLINK https://github.com/cefsharp/cefsharp }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0
\f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b62000000680074007400700073003a002f002f006700690074006800750062002e0063006f006d002f00630065006600730068006100720070002f00630065006600730068006100720070000000795881f43b1d7f48
af2c825dc485276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://github.com/cefsharp/cefsharp}}}\sectd \ltrsect
af2c825dc485276300000000a5ab00030020}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://github.com/cefsharp/cefsharp}}}\sectd \ltrsect
\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 for more information.}{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0
\b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \f3\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1
@ -97,28 +97,19 @@ af2c825dc485276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fc
\hich\af1\dbch\af31505\loch\f1 The Chromium Embedded Framework Authors. All rights reserved. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
\hich\af1\dbch\af31505\loch\f1 HYPERLINK https://bitbucket.org/chromiumembedded/cef/wiki/Home }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b82000000680074007400700073003a002f002f006200690074006200750063006b00650074002e006f00720067002f006300680072006f006d00690075006d0065006d006200650064006400650064002f0063006500
66002f00770069006b0069002f0048006f006d0065000000795881f43b1d7f48af2c825dc485276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1
66002f00770069006b0069002f0048006f006d0065000000795881f43b1d7f48af2c825dc485276300000000a5ab00030000}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1
https://bitbucket.org/chromiumembedded/cef/wiki/Home}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1
for more information.
\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \f3\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1
\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3 {\pntxtb \'b7}}\faauto\ls1\rin0\lin720\itap0\pararsid1002736 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
\hich\af1\dbch\af31505\loch\f1 Edge WebView2 Runtime\line }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 \hich\f1 Copyright \'a9\loch\f1 2019-}{\rtlch\fcs1
\af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid1848078 \hich\af1\dbch\af31505\loch\f1 2024}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1
Microsoft. All rights reserved. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1
HYPERLINK https://developer.microsoft.com/en-us/microsoft-edge/webview2/ }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b96000000680074007400700073003a002f002f0064006500760065006c006f007000650072002e006d006900630072006f0073006f00660074002e0063006f006d002f0065006e002d00750073002f006d0069006300
72006f0073006f00660074002d0065006400670065002f00770065006200760069006500770032002f000000795881f43b1d7f48af2c825dc485276300000000a5ab000320}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0
\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://developer.microsoft.com/en-us/microsoft-edge/webview2/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0
\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 for more information.
\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \f3\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1
\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3 {\pntxtb \'b7}}\faauto\ls1\rin0\lin720\itap0\pararsid1002736 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
\hich\af1\dbch\af31505\loch\f1 Font-Awesome-WPF}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \line \hich\af1\dbch\af31505\loch\f1 \hich\f1 Copyright \'a9\loch\f1 2014-}{\rtlch\fcs1
\af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid1848078 \hich\af1\dbch\af31505\loch\f1 2024}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1
charri. All rights reserved. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 HYPERLINK https://github.com/charri/Font-Awesome-WPF/ }{
\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b70000000680074007400700073003a002f002f006700690074006800750062002e0063006f006d002f006300680061007200720069002f0046006f006e0074002d0041007700650073006f006d0065002d0057005000
46002f000000795881f43b1d7f48af2c825dc485276300000000a5ab000369}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https\hich\af1\dbch\af31505\loch\f1
://github.com/charri/Font-Awesome-WPF/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 for more information.
46002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00036973}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1
https://github.com/charri/Font-Awesome-WPF/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 for more information.
\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \f3\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1
\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3 {\pntxtb \'b7}}\faauto\ls1\rin0\lin720\itap0\pararsid1002736 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
\hich\af1\dbch\af31505\loch\f1 KnownFolders}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \line \hich\af1\dbch\af31505\loch\f1 \hich\f1 Copyright \'a9\loch\f1 2017-}{\rtlch\fcs1 \af1\afs20
@ -126,7 +117,7 @@ HYPERLINK https://developer.microsoft.com/en-us/microsoft-edge/webview2/ }{\rtlc
Syroot. All rights reserved. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 HYPERLINK https://gitlab.com/Syroot/KnownFolders }{
\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b66000000680074007400700073003a002f002f006700690074006c00610062002e0063006f006d002f005300790072006f006f0074002f004b006e006f0077006e0046006f006c0064006500720073000000795881f4
3b1d7f48af2c825dc485276300000000a5ab000378}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://gitlab.com/Syroot/KnownFolders}}}\sectd \ltrsect
3b1d7f48af2c825dc485276300000000a5ab00037876}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://gitlab.com/Syroot/KnownFolders}}}\sectd \ltrsect
\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 for more information.
\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \f3\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1
\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3 {\pntxtb \'b7}}\faauto\ls1\rin0\lin720\itap0\pararsid1002736 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
@ -135,7 +126,7 @@ HYPERLINK https://developer.microsoft.com/en-us/microsoft-edge/webview2/ }{\rtlc
Mark Heath & contributors. All rights reserved. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 HYPERLINK https://github.com/naudio/NAudio
}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5a000000680074007400700073003a002f002f006700690074006800750062002e0063006f006d002f006e0061007500640069006f002f004e0041007500640069006f000000795881f43b1d7f48af2c825dc4852763
00000000a5ab000361}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://github.com/naudio/NAudio}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {
00000000a5ab00036170}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://github.com/naudio/NAudio}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {
\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 for more information.
\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \f3\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1
\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3 {\pntxtb \'b7}}\faauto\ls1\rin0\lin720\itap0\pararsid1002736 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
@ -144,7 +135,7 @@ HYPERLINK https://developer.microsoft.com/en-us/microsoft-edge/webview2/ }{\rtlc
James Newton-King. All rights reserved. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 HYPERLINK https://www.newtonsoft.com/json }{
\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b58000000680074007400700073003a002f002f007700770077002e006e006500770074006f006e0073006f00660074002e0063006f006d002f006a0073006f006e000000795881f43b1d7f48af2c825dc48527630000
0000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://www.newtonsoft.com/json}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {
0000a5ab00030000}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://www.newtonsoft.com/json}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {
\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 for more information.
\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \f3\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1
\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3 {\pntxtb \'b7}}\faauto\ls1\rin0\lin720\itap0\pararsid1002736 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
@ -153,13 +144,13 @@ HYPERLINK https://developer.microsoft.com/en-us/microsoft-edge/webview2/ }{\rtlc
DigiExam. All rights reserved. See }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 HYPERLINK https://github.com/DigiExam/simplewifi }{
\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang7\langfe2052\langnp7\insrsid1002736 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b66000000680074007400700073003a002f002f006700690074006800750062002e0063006f006d002f0044006900670069004500780061006d002f00730069006d0070006c00650077006900660069000000795881f4
3b1d7f48af2c825dc485276300000000a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://github.com/DigiExam/simplewifi}}}\sectd \ltrsect
3b1d7f48af2c825dc485276300000000a5ab00030000}}}{\fldrslt {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 https://github.com/DigiExam/simplewifi}}}\sectd \ltrsect
\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1 for more information.
\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \f3\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1
\nowidctlpar\wrapdefault{\*\pn \pnlvlblt\ilvl0\ls1\pnrnot0\pnf3 {\pntxtb \'b7}}\faauto\ls1\rin0\lin720\itap0\pararsid1002736 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
\hich\af1\dbch\af31505\loch\f1 Visual C++ Redistributable}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \line \hich\af1\dbch\af31505\loch\f1 \hich\f1 Copyright \'a9\loch\f1 1993-}{\rtlch\fcs1
\af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid1848078 \hich\af1\dbch\af31505\loch\f1 2024}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \hich\af1\dbch\af31505\loch\f1
Microsoft. All rights reserved.
\hich\af1\dbch\af31505\loch\f1 V\hich\af1\dbch\af31505\loch\f1 isual C++ Redistributable}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736 \line \hich\af1\dbch\af31505\loch\f1 \hich\f1 Copyright
\'a9\loch\f1 1993-}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid1848078 \hich\af1\dbch\af31505\loch\f1 2024}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2052\langnp1033\insrsid13781912\charrsid1002736
\hich\af1\dbch\af31505\loch\f1 Microsoft. All rights reserved.
\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
@ -304,8 +295,8 @@ fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e500000000000000000000000050bc
fcdf206fda01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e50000000000000000000000004010
c1461d90da01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000105000000000000}}

View file

@ -16,7 +16,6 @@
<Chain>
<PackageGroupRef Id="DotNetFramework" />
<PackageGroupRef Id="VisualCppRuntime" />
<PackageGroupRef Id="WebView2Runtime" />
<MsiPackage Id="SetupX64"
Compressed="yes"
DisplayInternalUI="no"

View file

@ -31,7 +31,6 @@
<Compile Include="Bundle.wxs" />
<Compile Include="DotNetFramework.wxs" />
<Compile Include="VisualCppRuntime.wxs" />
<Compile Include="WebView2Runtime.wxs" />
</ItemGroup>
<ItemGroup>
<WixExtension Include="WixNetFxExtension">

View file

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Fragment>
<util:RegistrySearch Root="HKLM"
Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"
Value="EBWebView"
Variable="RuntimeInstalled"
Win64="no" />
<PackageGroup Id="WebView2Runtime">
<ExePackage Compressed="yes"
DisplayName="Microsoft Edge WebView2 Runtime"
Name="MicrosoftEdgeWebview2Setup.exe"
PerMachine="yes"
Permanent="yes"
InstallCommand="/silent /install"
InstallCondition="NOT (REMOVE OR RuntimeInstalled)"
SourceFile="C:\Temp\MicrosoftEdgeWebview2Setup.exe"
Vital="yes">
<!-- Exit Code 0x8004090d: A newer version is already installed -->
<ExitCode Behavior="success" Value="-2147219187" />
</ExePackage>
</PackageGroup>
</Fragment>
</Wix>