2018-06-21 07:56:25 +02:00
|
|
|
|
/*
|
2022-01-21 16:33:52 +01:00
|
|
|
|
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
|
2018-06-21 07:56:25 +02:00
|
|
|
|
*
|
|
|
|
|
* 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.Concurrent;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using CefSharp;
|
2019-09-06 08:13:27 +02:00
|
|
|
|
using SafeExamBrowser.Browser.Contracts.Events;
|
2020-01-22 15:16:11 +01:00
|
|
|
|
using SafeExamBrowser.Browser.Events;
|
2019-08-30 09:55:26 +02:00
|
|
|
|
using SafeExamBrowser.Configuration.Contracts;
|
|
|
|
|
using SafeExamBrowser.Logging.Contracts;
|
2020-09-29 14:01:17 +02:00
|
|
|
|
using SafeExamBrowser.Settings.Browser;
|
2020-01-22 15:16:11 +01:00
|
|
|
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
2020-01-20 16:13:08 +01:00
|
|
|
|
using Syroot.Windows.IO;
|
2019-09-06 09:39:28 +02:00
|
|
|
|
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
2018-06-21 07:56:25 +02:00
|
|
|
|
|
|
|
|
|
namespace SafeExamBrowser.Browser.Handlers
|
|
|
|
|
{
|
|
|
|
|
internal class DownloadHandler : IDownloadHandler
|
|
|
|
|
{
|
2018-06-29 09:50:20 +02:00
|
|
|
|
private AppConfig appConfig;
|
2018-06-21 07:56:25 +02:00
|
|
|
|
private BrowserSettings settings;
|
2020-09-29 14:01:17 +02:00
|
|
|
|
private WindowSettings windowSettings;
|
2018-06-21 07:56:25 +02:00
|
|
|
|
private ConcurrentDictionary<int, DownloadFinishedCallback> callbacks;
|
2020-01-22 15:16:11 +01:00
|
|
|
|
private ConcurrentDictionary<int, Guid> downloads;
|
2018-08-31 15:29:36 +02:00
|
|
|
|
private ILogger logger;
|
2018-06-21 07:56:25 +02:00
|
|
|
|
|
2020-01-20 16:13:08 +01:00
|
|
|
|
internal event DownloadRequestedEventHandler ConfigurationDownloadRequested;
|
2020-01-22 15:16:11 +01:00
|
|
|
|
internal event DownloadUpdatedEventHandler DownloadUpdated;
|
2018-06-21 07:56:25 +02:00
|
|
|
|
|
2020-09-29 14:01:17 +02:00
|
|
|
|
internal DownloadHandler(AppConfig appConfig, ILogger logger, BrowserSettings settings, WindowSettings windowSettings)
|
2018-06-21 07:56:25 +02:00
|
|
|
|
{
|
2018-06-29 09:50:20 +02:00
|
|
|
|
this.appConfig = appConfig;
|
2018-06-21 07:56:25 +02:00
|
|
|
|
this.callbacks = new ConcurrentDictionary<int, DownloadFinishedCallback>();
|
2020-01-22 15:16:11 +01:00
|
|
|
|
this.downloads = new ConcurrentDictionary<int, Guid>();
|
2018-08-31 15:29:36 +02:00
|
|
|
|
this.logger = logger;
|
2018-06-21 07:56:25 +02:00
|
|
|
|
this.settings = settings;
|
2020-09-29 14:01:17 +02:00
|
|
|
|
this.windowSettings = windowSettings;
|
2018-06-21 07:56:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 14:30:38 +01:00
|
|
|
|
public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback)
|
2018-06-21 07:56:25 +02:00
|
|
|
|
{
|
|
|
|
|
var uri = new Uri(downloadItem.Url);
|
2020-02-06 11:10:22 +01:00
|
|
|
|
var uriExtension = Path.GetExtension(uri.AbsolutePath);
|
|
|
|
|
var fileExtension = Path.GetExtension(downloadItem.SuggestedFileName);
|
|
|
|
|
var isConfigurationFile = false;
|
|
|
|
|
|
|
|
|
|
isConfigurationFile |= string.Equals(appConfig.ConfigurationFileExtension, fileExtension, StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
isConfigurationFile |= string.Equals(appConfig.ConfigurationFileExtension, uriExtension, StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
isConfigurationFile |= string.Equals(appConfig.ConfigurationFileMimeType, downloadItem.MimeType, StringComparison.OrdinalIgnoreCase);
|
2018-06-21 07:56:25 +02:00
|
|
|
|
|
2020-09-29 14:01:17 +02:00
|
|
|
|
logger.Debug($"Detected download request{(windowSettings.UrlPolicy.CanLog() ? $" for '{uri}'" : "")}.");
|
2018-08-31 15:29:36 +02:00
|
|
|
|
|
2020-02-06 11:10:22 +01:00
|
|
|
|
if (isConfigurationFile)
|
2018-06-21 07:56:25 +02:00
|
|
|
|
{
|
|
|
|
|
Task.Run(() => RequestConfigurationFileDownload(downloadItem, callback));
|
|
|
|
|
}
|
2018-08-31 15:29:36 +02:00
|
|
|
|
else if (settings.AllowDownloads)
|
2018-06-21 07:56:25 +02:00
|
|
|
|
{
|
2020-01-20 16:13:08 +01:00
|
|
|
|
Task.Run(() => HandleFileDownload(downloadItem, callback));
|
2018-06-21 07:56:25 +02:00
|
|
|
|
}
|
2018-08-31 15:29:36 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
2020-09-29 14:01:17 +02:00
|
|
|
|
logger.Info($"Aborted download request{(windowSettings.UrlPolicy.CanLog() ? $" for '{uri}'" : "")}, as downloading is not allowed.");
|
2018-08-31 15:29:36 +02:00
|
|
|
|
}
|
2018-06-21 07:56:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 14:30:38 +01:00
|
|
|
|
public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
|
2018-06-21 07:56:25 +02:00
|
|
|
|
{
|
2020-01-22 15:16:11 +01:00
|
|
|
|
var hasId = downloads.TryGetValue(downloadItem.Id, out var id);
|
|
|
|
|
|
|
|
|
|
if (hasId)
|
|
|
|
|
{
|
|
|
|
|
var state = new DownloadItemState(id)
|
|
|
|
|
{
|
|
|
|
|
Completion = downloadItem.PercentComplete / 100.0,
|
|
|
|
|
FullPath = downloadItem.FullPath,
|
|
|
|
|
IsCancelled = downloadItem.IsCancelled,
|
|
|
|
|
IsComplete = downloadItem.IsComplete,
|
|
|
|
|
Url = downloadItem.Url
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Task.Run(() => DownloadUpdated?.Invoke(state));
|
|
|
|
|
}
|
2020-01-20 16:13:08 +01:00
|
|
|
|
|
2018-06-21 07:56:25 +02:00
|
|
|
|
if (downloadItem.IsComplete || downloadItem.IsCancelled)
|
|
|
|
|
{
|
2020-09-29 14:01:17 +02:00
|
|
|
|
logger.Debug($"Download of '{downloadItem.FullPath}' {(downloadItem.IsComplete ? "is complete" : "was cancelled")}.");
|
2020-01-22 15:16:11 +01:00
|
|
|
|
|
2018-06-21 07:56:25 +02:00
|
|
|
|
if (callbacks.TryRemove(downloadItem.Id, out DownloadFinishedCallback finished) && finished != null)
|
|
|
|
|
{
|
2020-09-24 12:55:20 +02:00
|
|
|
|
Task.Run(() => finished.Invoke(downloadItem.IsComplete, downloadItem.Url, downloadItem.FullPath));
|
2018-06-21 07:56:25 +02:00
|
|
|
|
}
|
2018-08-31 15:29:36 +02:00
|
|
|
|
|
2020-01-22 15:16:11 +01:00
|
|
|
|
if (hasId)
|
|
|
|
|
{
|
|
|
|
|
downloads.TryRemove(downloadItem.Id, out _);
|
|
|
|
|
}
|
2020-01-20 16:13:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void HandleFileDownload(DownloadItem downloadItem, IBeforeDownloadCallback callback)
|
|
|
|
|
{
|
|
|
|
|
var filePath = default(string);
|
2020-08-05 22:55:38 +02:00
|
|
|
|
var showDialog = settings.AllowCustomDownAndUploadLocation;
|
2020-01-20 16:13:08 +01:00
|
|
|
|
|
|
|
|
|
logger.Debug($"Handling download of file '{downloadItem.SuggestedFileName}'.");
|
|
|
|
|
|
2020-08-05 22:55:38 +02:00
|
|
|
|
if (!string.IsNullOrEmpty(settings.DownAndUploadDirectory))
|
2020-01-20 16:13:08 +01:00
|
|
|
|
{
|
2020-08-05 22:55:38 +02:00
|
|
|
|
filePath = Path.Combine(Environment.ExpandEnvironmentVariables(settings.DownAndUploadDirectory), downloadItem.SuggestedFileName);
|
2020-01-20 16:13:08 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
filePath = Path.Combine(KnownFolders.Downloads.ExpandedPath, downloadItem.SuggestedFileName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (showDialog)
|
|
|
|
|
{
|
|
|
|
|
logger.Debug($"Allowing user to select custom download location, with '{filePath}' as suggestion.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Debug($"Automatically downloading file as '{filePath}'.");
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-22 15:16:11 +01:00
|
|
|
|
downloads[downloadItem.Id] = Guid.NewGuid();
|
|
|
|
|
|
2020-01-20 16:13:08 +01:00
|
|
|
|
using (callback)
|
|
|
|
|
{
|
|
|
|
|
callback.Continue(filePath, showDialog);
|
2018-06-21 07:56:25 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RequestConfigurationFileDownload(DownloadItem downloadItem, IBeforeDownloadCallback callback)
|
|
|
|
|
{
|
2020-09-10 12:35:58 +02:00
|
|
|
|
var args = new DownloadEventArgs { Url = downloadItem.Url };
|
2018-06-21 07:56:25 +02:00
|
|
|
|
|
2020-01-20 16:13:08 +01:00
|
|
|
|
logger.Debug($"Handling download of configuration file '{downloadItem.SuggestedFileName}'.");
|
2018-06-21 07:56:25 +02:00
|
|
|
|
ConfigurationDownloadRequested?.Invoke(downloadItem.SuggestedFileName, args);
|
|
|
|
|
|
|
|
|
|
if (args.AllowDownload)
|
|
|
|
|
{
|
|
|
|
|
if (args.Callback != null)
|
|
|
|
|
{
|
|
|
|
|
callbacks[downloadItem.Id] = args.Callback;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-20 16:13:08 +01:00
|
|
|
|
logger.Debug($"Starting download of configuration file '{downloadItem.SuggestedFileName}'...");
|
2019-09-04 15:12:59 +02:00
|
|
|
|
|
2018-06-21 07:56:25 +02:00
|
|
|
|
using (callback)
|
|
|
|
|
{
|
|
|
|
|
callback.Continue(args.DownloadPath, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-04 15:12:59 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
2020-01-20 16:13:08 +01:00
|
|
|
|
logger.Debug($"Download of configuration file '{downloadItem.SuggestedFileName}' was cancelled.");
|
2019-09-04 15:12:59 +02:00
|
|
|
|
}
|
2018-06-21 07:56:25 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|