2018-03-08 07:35:58 +01:00
/ *
2023-03-08 00:30:20 +01:00
* Copyright ( c ) 2023 ETH Zürich , Educational Development and Technology ( LET )
2018-03-08 07:35:58 +01: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/.
* /
2019-12-18 08:09:59 +01:00
using System ;
2021-04-13 17:12:09 +02:00
using System.IO ;
2021-08-31 18:15:26 +02:00
using System.Net ;
2020-03-02 10:46:42 +01:00
using System.Text.RegularExpressions ;
2018-03-08 07:35:58 +01:00
using CefSharp ;
2019-09-13 09:17:14 +02:00
using SafeExamBrowser.Browser.Contracts.Filters ;
2019-09-12 12:31:17 +02:00
using SafeExamBrowser.Browser.Events ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.Configuration.Contracts ;
2019-09-06 08:13:27 +02:00
using SafeExamBrowser.Logging.Contracts ;
2020-09-29 14:01:17 +02:00
using SafeExamBrowser.Settings.Browser ;
2019-12-18 08:24:55 +01:00
using SafeExamBrowser.Settings.Browser.Filter ;
2019-12-18 08:09:59 +01:00
using BrowserSettings = SafeExamBrowser . Settings . Browser . BrowserSettings ;
2020-02-14 09:51:52 +01:00
using Request = SafeExamBrowser . Browser . Contracts . Filters . Request ;
2018-03-08 07:35:58 +01:00
namespace SafeExamBrowser.Browser.Handlers
{
2019-08-23 15:48:23 +02:00
internal class RequestHandler : CefSharp . Handler . RequestHandler
2018-03-08 07:35:58 +01:00
{
2022-02-23 13:59:36 +01:00
private readonly AppConfig appConfig ;
private readonly IRequestFilter filter ;
private readonly ILogger logger ;
private readonly ResourceHandler resourceHandler ;
private readonly WindowSettings windowSettings ;
private readonly BrowserSettings settings ;
2020-03-02 10:46:42 +01:00
private string quitUrlPattern ;
2019-09-12 12:31:17 +02:00
2019-12-19 15:02:40 +01:00
internal event UrlEventHandler QuitUrlVisited ;
internal event UrlEventHandler RequestBlocked ;
2018-08-14 09:06:35 +02:00
2020-08-03 14:41:25 +02:00
internal RequestHandler (
AppConfig appConfig ,
IRequestFilter filter ,
ILogger logger ,
ResourceHandler resourceHandler ,
2020-09-29 14:01:17 +02:00
BrowserSettings settings ,
2022-02-23 13:59:36 +01:00
WindowSettings windowSettings )
2018-08-14 09:06:35 +02:00
{
2021-04-13 17:12:09 +02:00
this . appConfig = appConfig ;
2019-09-13 09:17:14 +02:00
this . filter = filter ;
2019-09-12 12:31:17 +02:00
this . logger = logger ;
2020-08-03 14:41:25 +02:00
this . resourceHandler = resourceHandler ;
2020-09-29 14:01:17 +02:00
this . settings = settings ;
this . windowSettings = windowSettings ;
2019-09-06 08:13:27 +02:00
}
2022-02-23 13:59:36 +01:00
protected override bool GetAuthCredentials ( IWebBrowser webBrowser , IBrowser browser , string originUrl , bool isProxy , string host , int port , string realm , string scheme , IAuthCallback callback )
2019-12-18 08:09:59 +01:00
{
if ( isProxy )
{
foreach ( var proxy in settings . Proxy . Proxies )
{
if ( proxy . RequiresAuthentication & & host ? . Equals ( proxy . Host , StringComparison . OrdinalIgnoreCase ) = = true & & port = = proxy . Port )
{
callback . Continue ( proxy . Username , proxy . Password ) ;
return true ;
}
}
}
return base . GetAuthCredentials ( webBrowser , browser , originUrl , isProxy , host , port , realm , scheme , callback ) ;
}
2022-02-23 13:59:36 +01:00
protected override IResourceRequestHandler GetResourceRequestHandler ( IWebBrowser webBrowser , IBrowser browser , IFrame frame , IRequest request , bool isNavigation , bool isDownload , string requestInitiator , ref bool disableDefaultHandling )
2019-04-18 08:15:47 +02:00
{
2019-09-10 11:01:49 +02:00
return resourceHandler ;
2018-03-08 07:35:58 +01:00
}
2019-09-12 12:31:17 +02:00
2019-12-18 08:09:59 +01:00
protected override bool OnBeforeBrowse ( IWebBrowser webBrowser , IBrowser browser , IFrame frame , IRequest request , bool userGesture , bool isRedirect )
2019-09-12 12:31:17 +02:00
{
2019-12-19 15:02:40 +01:00
if ( IsQuitUrl ( request ) )
{
QuitUrlVisited ? . Invoke ( request . Url ) ;
return true ;
}
2019-09-12 12:31:17 +02:00
if ( Block ( request ) )
{
2020-03-04 10:08:34 +01:00
if ( request . ResourceType = = ResourceType . MainFrame )
{
RequestBlocked ? . Invoke ( request . Url ) ;
}
2019-09-12 12:31:17 +02:00
return true ;
}
2021-04-13 17:12:09 +02:00
if ( IsConfigurationFile ( request , out var downloadUrl ) )
{
browser . GetHost ( ) . StartDownload ( downloadUrl ) ;
return true ;
}
2019-12-18 08:09:59 +01:00
return base . OnBeforeBrowse ( webBrowser , browser , frame , request , userGesture , isRedirect ) ;
2019-09-12 12:31:17 +02:00
}
2022-02-23 13:59:36 +01:00
protected override bool OnOpenUrlFromTab ( IWebBrowser webBrowser , IBrowser browser , IFrame frame , string targetUrl , WindowOpenDisposition targetDisposition , bool userGesture )
2021-02-15 23:47:34 +01:00
{
switch ( targetDisposition )
{
case WindowOpenDisposition . NewBackgroundTab :
case WindowOpenDisposition . NewPopup :
case WindowOpenDisposition . NewWindow :
case WindowOpenDisposition . SaveToDisk :
return true ;
default :
return base . OnOpenUrlFromTab ( webBrowser , browser , frame , targetUrl , targetDisposition , userGesture ) ;
}
}
2021-04-13 17:12:09 +02:00
private bool IsConfigurationFile ( IRequest request , out string downloadUrl )
{
2021-08-24 15:23:17 +02:00
var isValidUri = Uri . TryCreate ( request . Url , UriKind . RelativeOrAbsolute , out var uri ) ;
var isConfigurationFile = isValidUri & & string . Equals ( appConfig . ConfigurationFileExtension , Path . GetExtension ( uri . AbsolutePath ) , StringComparison . OrdinalIgnoreCase ) ;
2021-04-13 17:12:09 +02:00
downloadUrl = request . Url ;
if ( isConfigurationFile )
{
if ( uri . Scheme = = appConfig . SebUriScheme )
{
downloadUrl = new UriBuilder ( uri ) { Scheme = Uri . UriSchemeHttp } . Uri . AbsoluteUri ;
}
else if ( uri . Scheme = = appConfig . SebUriSchemeSecure )
{
downloadUrl = new UriBuilder ( uri ) { Scheme = Uri . UriSchemeHttps } . Uri . AbsoluteUri ;
}
logger . Debug ( $"Detected configuration file {(windowSettings.UrlPolicy.CanLog() ? $" ' { uri } ' " : " ")}." ) ;
}
return isConfigurationFile ;
}
2019-12-19 15:02:40 +01:00
private bool IsQuitUrl ( IRequest request )
{
2020-03-02 10:46:42 +01:00
var isQuitUrl = false ;
2019-12-19 15:02:40 +01:00
2020-03-02 10:46:42 +01:00
if ( ! string . IsNullOrWhiteSpace ( settings . QuitUrl ) )
2019-12-19 15:02:40 +01:00
{
2022-02-23 13:59:36 +01:00
if ( quitUrlPattern = = default )
2020-03-02 10:46:42 +01:00
{
quitUrlPattern = Regex . Escape ( settings . QuitUrl . TrimEnd ( '/' ) ) + @"\/?" ;
}
isQuitUrl = Regex . IsMatch ( request . Url , quitUrlPattern , RegexOptions . IgnoreCase ) ;
if ( isQuitUrl )
{
2020-09-29 14:01:17 +02:00
logger . Debug ( $"Detected quit URL{(windowSettings.UrlPolicy.CanLog() ? $" ' { request . Url } ' " : " ")}." ) ;
2020-03-02 10:46:42 +01:00
}
2019-12-19 15:02:40 +01:00
}
return isQuitUrl ;
}
2019-09-12 12:31:17 +02:00
private bool Block ( IRequest request )
{
2020-03-04 10:08:34 +01:00
var block = false ;
2021-08-31 18:15:26 +02:00
var url = WebUtility . UrlDecode ( request . Url ) ;
var isValidUrl = Uri . TryCreate ( url , UriKind . Absolute , out _ ) ;
2020-03-04 10:08:34 +01:00
2021-08-31 18:15:26 +02:00
if ( settings . Filter . ProcessMainRequests & & request . ResourceType = = ResourceType . MainFrame & & isValidUrl )
2019-09-12 12:31:17 +02:00
{
2021-08-31 18:15:26 +02:00
var result = filter . Process ( new Request { Url = url } ) ;
2019-09-12 12:31:17 +02:00
2021-02-05 16:26:40 +01:00
// We apparently can't filter chrome extension requests, as this prevents the rendering of PDFs.
2021-08-31 18:15:26 +02:00
if ( result = = FilterResult . Block & & ! url . StartsWith ( "chrome-extension://" ) )
2019-09-12 12:31:17 +02:00
{
2020-03-04 10:08:34 +01:00
block = true ;
2021-08-31 18:15:26 +02:00
logger . Info ( $"Blocked main request{(windowSettings.UrlPolicy.CanLog() ? $" for ' { url } ' " : " ")} ({request.ResourceType}, {request.TransitionType})." ) ;
2019-09-12 12:31:17 +02:00
}
2020-03-04 10:08:34 +01:00
}
2019-09-12 12:31:17 +02:00
2021-08-31 18:15:26 +02:00
if ( settings . Filter . ProcessContentRequests & & request . ResourceType ! = ResourceType . MainFrame & & isValidUrl )
2020-03-04 10:08:34 +01:00
{
2021-08-31 18:15:26 +02:00
var result = filter . Process ( new Request { Url = url } ) ;
2020-03-04 10:08:34 +01:00
if ( result = = FilterResult . Block )
{
block = true ;
2021-08-31 18:15:26 +02:00
logger . Info ( $"Blocked content request{(windowSettings.UrlPolicy.CanLog() ? $" for ' { url } ' " : " ")} ({request.ResourceType}, {request.TransitionType})." ) ;
2020-03-04 10:08:34 +01:00
}
2019-09-12 12:31:17 +02:00
}
2021-08-31 18:15:26 +02:00
if ( ! isValidUrl )
{
logger . Warn ( $"Filter could not process request{(windowSettings.UrlPolicy.CanLog() ? $" for ' { url } ' " : " ")} ({request.ResourceType}, {request.TransitionType})!" ) ;
}
2020-03-04 10:08:34 +01:00
return block ;
2019-09-12 12:31:17 +02:00
}
2018-03-08 07:35:58 +01:00
}
}