mirror of
https://github.com/Jackett/Jackett.git
synced 2025-09-12 15:04:13 +02:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2ca7975d3d | ||
![]() |
e8dbaa4a8e | ||
![]() |
a6fd4c3da1 | ||
![]() |
1b3365d8ad | ||
![]() |
a822b3c4f4 | ||
![]() |
ff663cc202 | ||
![]() |
4c3dbb4746 | ||
![]() |
a4ba0d21d9 | ||
![]() |
859da99442 | ||
![]() |
1cbeb74c8e | ||
![]() |
4ae705eb15 | ||
![]() |
b469c2e764 | ||
![]() |
2712ff432b | ||
![]() |
9da64d5bbf | ||
![]() |
5d21a69b07 | ||
![]() |
a083090b5e | ||
![]() |
27ae1b3e82 | ||
![]() |
f0245900e4 |
@@ -336,7 +336,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Torrent.LT
|
||||
* TorrentBD
|
||||
* TorrentBytes (TBy)
|
||||
* TorrentCCF (TCCF) [![(invite needed)][inviteneeded]](#)
|
||||
* TorrentCCF (TCCF)
|
||||
* TorrentDay (TD)
|
||||
* Torrentech (TTH)
|
||||
* TorrentHeaven
|
||||
|
@@ -99,8 +99,8 @@ function loadJackettSettings() {
|
||||
doNotify(value, "danger", "glyphicon glyphicon-alert", false);
|
||||
})
|
||||
|
||||
proxyWarning(data.proxy_url);
|
||||
reloadIndexers();
|
||||
proxyWarning(data.proxy_url);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1229,8 +1229,11 @@ function bindUIButtons() {
|
||||
}
|
||||
|
||||
function proxyWarning(input) {
|
||||
if (input && input.trim() != "")
|
||||
if (input != null && input.trim() !== "") {
|
||||
$('#proxy-warning').show();
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#proxy-warning').hide();
|
||||
}
|
||||
}
|
||||
|
@@ -34,8 +34,8 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../bootstrap/bootstrap.min.css?changed=2017083001">
|
||||
<link rel="stylesheet" type="text/css" href="../animate.css?changed=2017083001">
|
||||
<link rel="stylesheet" type="text/css" href="../custom.css?changed=20190331" media="only screen and (min-device-width: 480px)">
|
||||
<link rel="stylesheet" type="text/css" href="../custom_mobile.css?changed=20190331" media="only screen and (max-device-width: 480px)">
|
||||
<link rel="stylesheet" type="text/css" href="../custom.css?changed=20190401" media="only screen and (min-device-width: 480px)">
|
||||
<link rel="stylesheet" type="text/css" href="../custom_mobile.css?changed=20190401" media="only screen and (max-device-width: 480px)">
|
||||
<link rel="stylesheet" type="text/css" href="../css/jquery.dataTables.min.css?changed=2017083001">
|
||||
<link rel="stylesheet" type="text/css" href="../css/bootstrap-multiselect.css?changed=2017083001" />
|
||||
<link rel="stylesheet" type="text/css" href="../css/font-awesome.min.css?changed=2017083001">
|
||||
@@ -671,6 +671,6 @@
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="../libs/api.js?changed=2017083001"></script>
|
||||
<script type="text/javascript" src="../custom.js?changed=20190331"></script>
|
||||
<script type="text/javascript" src="../custom.js?changed=20190401"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -143,7 +143,7 @@
|
||||
- selector: div.alert-error
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href="account-logout.php"]
|
||||
# selector: a[href="account-logout.php"]
|
||||
|
||||
ratio:
|
||||
path: index.php
|
||||
@@ -169,13 +169,13 @@
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
incldead: 0
|
||||
incldead: 1
|
||||
freeleech: 0
|
||||
lang: 0
|
||||
sort: "id"
|
||||
order: "desc"
|
||||
rows:
|
||||
selector: table.table-striped > tbody > tr:has(a[href^="torrents-details.php?id="])
|
||||
selector: table > tbody > tr:has(a[href^="torrents-details.php?id="])
|
||||
fields:
|
||||
title:
|
||||
selector: a[href^="torrents-details.php?id="]
|
||||
@@ -293,14 +293,14 @@
|
||||
date:
|
||||
text: now
|
||||
size:
|
||||
selector: td:nth-child(2) span.label-info
|
||||
selector: td:nth-child(2) span.badge-info
|
||||
seeders:
|
||||
selector: td:nth-child(4)
|
||||
leechers:
|
||||
selector: td:nth-child(5)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"span.label-success i:contains(\"FREE\")": "0"
|
||||
"span.badge-success:contains(\"FREE\")": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
|
@@ -78,10 +78,10 @@
|
||||
- name: downloadlink
|
||||
type: select
|
||||
label: Download link
|
||||
default: "magnet:"
|
||||
default: "magnet:?xt="
|
||||
options:
|
||||
"/download/" : ".torrent"
|
||||
"magnet:": "magnet"
|
||||
"/get/key:" : ".torrent"
|
||||
"magnet:?xt=": "magnet"
|
||||
|
||||
login:
|
||||
path: /
|
||||
@@ -100,7 +100,7 @@
|
||||
selector: a[href="/users/logout/"]
|
||||
|
||||
download:
|
||||
selector: a[href^="{{ .Config.downloadlink }}"]
|
||||
selector: a[href*="{{ .Config.downloadlink }}"]
|
||||
|
||||
search:
|
||||
paths:
|
||||
|
@@ -67,7 +67,7 @@
|
||||
filters:
|
||||
# now we put the date at the right place according scene naming rules using .Result.site_date
|
||||
- name: replace
|
||||
args: ["FRENCH", "{{ .Result.site_date }} FRENCH"]
|
||||
args: [" FRENCH", " {{ .Result.site_date }} FRENCH"]
|
||||
- name: replace
|
||||
args: ["TRUEFRENCH", "{{ .Result.site_date }} TRUEFRENCH"]
|
||||
- name: replace
|
||||
|
@@ -16,21 +16,28 @@
|
||||
categorymappings:
|
||||
- {id: films, cat: Movies, desc: "Movies"}
|
||||
- {id: series, cat: TV, desc: "TV"}
|
||||
- {id: musique, cat: Audio, desc: "Music"}
|
||||
- {id: ebook, cat: Books, desc: "Ebook"}
|
||||
- {id: logiciels, cat: PC, desc: "Software"}
|
||||
- {id: jeux-pc, cat: PC/Games, desc: "PC Games"}
|
||||
- {id: jeux-consoles, cat: Console, desc: "Console Games"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
settings: []
|
||||
|
||||
download:
|
||||
selector: div#btn-download a
|
||||
selector: div.btn-download a
|
||||
attribute: href
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "{{if .Keywords}}recherche/{{.Keywords}}{{else}}{{end}}"
|
||||
rows:
|
||||
selector: div#gauche > table > tbody > tr:has(a)
|
||||
selector: table.table-corps > tbody > tr:has(a)
|
||||
fields:
|
||||
site_date:
|
||||
selector: a
|
||||
@@ -43,7 +50,7 @@
|
||||
filters:
|
||||
# now we put the date at the right place according scene naming rules using .Result.site_date
|
||||
- name: replace
|
||||
args: ["FRENCH", "{{ .Result.site_date }} FRENCH"]
|
||||
args: [" FRENCH", " {{ .Result.site_date }} FRENCH"]
|
||||
- name: replace
|
||||
args: ["TRUEFRENCH", "{{ .Result.site_date }} TRUEFRENCH"]
|
||||
- name: replace
|
||||
@@ -61,21 +68,21 @@
|
||||
selector: div.poid
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: [ "\\.(\\d) Ko", "$1X00"]
|
||||
args: [ "\\.(\\d)Ko", "$1X00"]
|
||||
- name: re_replace
|
||||
args: [ " Ko", "000"]
|
||||
args: [ "Ko", "000"]
|
||||
- name: re_replace
|
||||
args: [ "\\.(\\d) Mo", "$1X00000"]
|
||||
args: [ "\\.(\\d)Mo", "$1X00000"]
|
||||
- name: re_replace
|
||||
args: [ " Mo", "000000"]
|
||||
args: [ "Mo", "000000"]
|
||||
- name: re_replace
|
||||
args: [ "\\.(\\d) Go", "$1X00000000"]
|
||||
args: [ "\\.(\\d)Go", "$1X00000000"]
|
||||
- name: re_replace
|
||||
args: [ " Go", "000000000"]
|
||||
args: [ "Go", "000000000"]
|
||||
- name: re_replace
|
||||
args: [ "\\.(\\d) To", "$1X00000000000"]
|
||||
args: [ "\\.(\\d)To", "$1X00000000000"]
|
||||
- name: re_replace
|
||||
args: [ " To", "000000000000"]
|
||||
args: [ "To", "000000000000"]
|
||||
- name: replace
|
||||
args: [ "X", "" ]
|
||||
date:
|
||||
|
@@ -6,6 +6,8 @@
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://www.elitetorrent.io/
|
||||
legacylinks:
|
||||
- https://www.elitetorrent.biz/
|
||||
|
||||
caps:
|
||||
|
@@ -63,7 +63,7 @@
|
||||
filters:
|
||||
# now we put the date at the right place according scene naming rules using .Result.site_date
|
||||
- name: replace
|
||||
args: ["FRENCH", "{{ .Result.site_date }} FRENCH"]
|
||||
args: [" FRENCH", " {{ .Result.site_date }} FRENCH"]
|
||||
- name: replace
|
||||
args: ["TRUEFRENCH", "{{ .Result.site_date }} TRUEFRENCH"]
|
||||
- name: replace
|
||||
|
@@ -86,6 +86,10 @@
|
||||
inputs:
|
||||
uid: "{{ .Config.username }}"
|
||||
pwd: "{{ .Config.password }}"
|
||||
captcha:
|
||||
type: image
|
||||
selector: img[src^="access_code/"]
|
||||
input: private_key
|
||||
error:
|
||||
- selector: tr td span[style="color:#FF0000;"]
|
||||
test:
|
||||
|
@@ -53,8 +53,9 @@
|
||||
- path: filterTorrents
|
||||
inputs:
|
||||
search: "{{ .Keywords }}"
|
||||
description: ""
|
||||
uploader: ""
|
||||
sort: created_at
|
||||
sorting: created_at
|
||||
direction: desc
|
||||
qty: 100
|
||||
rows:
|
||||
|
@@ -67,24 +67,16 @@
|
||||
date:
|
||||
selector: td:nth-child(4)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["heures", "hours"]
|
||||
- name: replace
|
||||
args: ["heure", "hour"]
|
||||
- name: replace
|
||||
args: ["jours", "days"]
|
||||
- name: replace
|
||||
args: ["jour", "day"]
|
||||
- name: replace
|
||||
args: ["semaines", "weeks"]
|
||||
- name: replace
|
||||
args: ["semaine", "week"]
|
||||
- name: replace
|
||||
args: ["mois", "months"]
|
||||
args: ["moi", "month"]
|
||||
- name: replace
|
||||
args: ["ans", "years"]
|
||||
- name: replace
|
||||
args: ["an", "year"]
|
||||
args: [" an", " year"]
|
||||
- name: append
|
||||
args: " ago"
|
||||
size:
|
||||
|
@@ -158,11 +158,36 @@
|
||||
selector: td:nth-child(7)
|
||||
grabs:
|
||||
selector: td:nth-child(8)
|
||||
date:
|
||||
selector: td:nth-child(9) img
|
||||
attribute: alt
|
||||
filters:
|
||||
- name: timeago
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
date:
|
||||
selector: td:nth-child(9)
|
||||
filters:
|
||||
filters:
|
||||
# translations for Turkish|Estonian|Danish|Italian|Polish|Norwegian|Portoguese|Czech|Russian|Romanian|Spanish|French|German|Bulgarian|Dutch
|
||||
- name: re_replace
|
||||
args: ["(?i)(dakika|minut|minuto|minuta|minutt|минута|Minute|minuut)", "minute"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(dakika|minutit|minutter|minuti|minuty|minutos|минуты|минут|Minuten|минути|minuten)", "minutes"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(saat|tund|time|ora|godzina|hora|hodina|час|oră|heure|Stunde|uur)", "hour"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(saat|tundi|timer|ore|godziny|horas|hodiny|hoden|часа|часов|ore|heures|Stunden)", "hours"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(gün|päev|dag|giorno|dzień|dia|den|день|zi|día|jour|Tag|ден)", "day"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(gün|päeva|dage|giorni|dni|dias|dny|дня|дней|zile|días|jours|Tagen|дни|dagen)", "days"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(hafta|nädal|uge|settimana|tydzień|uke|semana|týden|неделю|săptămână|semaine|Woche|седмица)", "week"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(hafta|nädalat|uger|settimane|tygodnie|uker|semanas|týdny|недели|недель|săptămâni|semaines|Wochen|седмици|weken)", "weeks"]
|
||||
- name: re_replace
|
||||
args: ["(?i) (ay|kuu|måned|mese|miesiąc|mês|měsíc|месяц|lună|mes|mois|Monat|месец|maand)", "month"]
|
||||
- name: re_replace
|
||||
args: ["(?i) (ay|kuud|måneder|mesi|miesiące|meses|měsíce|месяца|месяцев|luni|meses|mois|Monaten|месеца|maanden)", "months"]
|
||||
- name: append
|
||||
args: " ago"
|
||||
downloadvolumefactor:
|
||||
text: "1"
|
||||
uploadvolumefactor:
|
||||
|
@@ -63,7 +63,7 @@
|
||||
filters:
|
||||
# now we put the date at the right place according scene naming rules using .Result.site_date
|
||||
- name: replace
|
||||
args: ["FRENCH", "{{ .Result.site_date }} FRENCH"]
|
||||
args: [" FRENCH", " {{ .Result.site_date }} FRENCH"]
|
||||
- name: replace
|
||||
args: ["TRUEFRENCH", "{{ .Result.site_date }} TRUEFRENCH"]
|
||||
- name: replace
|
||||
|
@@ -72,7 +72,7 @@
|
||||
filters:
|
||||
# now we put the date at the right place according scene naming rules using .Result.site_date
|
||||
- name: replace
|
||||
args: ["FRENCH", "{{ .Result.site_date }} FRENCH"]
|
||||
args: [" FRENCH", " {{ .Result.site_date }} FRENCH"]
|
||||
- name: replace
|
||||
args: ["TRUEFRENCH", "{{ .Result.site_date }} TRUEFRENCH"]
|
||||
- name: replace
|
||||
|
@@ -139,6 +139,10 @@
|
||||
type: checkbox
|
||||
label: Enhance sonarr compatibility with anime by renaming episode (xxx to exxx). Works only if episode is at the end of the query. Can disturb movies search. (back to the future 3 -> back to the future e3)
|
||||
default: false
|
||||
- name: sonarrv3hack
|
||||
type: checkbox
|
||||
label: "Enable Full season search hack: Sonarrv3 send 'Series Name SXX' but it won't match 'Series Name - Saison 01' for example so we remove the 'SXX' ==> 'Series Name'"
|
||||
default: true
|
||||
|
||||
login:
|
||||
method: form
|
||||
@@ -159,7 +163,7 @@
|
||||
keywordsfilters:
|
||||
# Full season seach hack: Sonarrv3 send 'Series Name SXX' but it won't match 'Series Name - Saison 01' for example so we remove the 'SXX' ==> 'Series Name'
|
||||
- name: re_replace
|
||||
args: ["(.*)[sS](\\d{1,4})$", "$1"]
|
||||
args: ["(.*)[sS](\\d{1,4})$", "{{ if .Config.sonarrv3hack }}$1{{else}}$1S$2{{end}}"]
|
||||
- name: replace
|
||||
args: ["\"", ""]
|
||||
- name: trim
|
||||
|
@@ -14,6 +14,7 @@ using Jackett.Common.Models.Config;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using NLog;
|
||||
using Jackett.Common.Helpers;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Jackett.Common.Utils.Clients
|
||||
{
|
||||
@@ -23,6 +24,34 @@ namespace Jackett.Common.Utils.Clients
|
||||
static protected string webProxyUrl;
|
||||
static protected IWebProxy webProxy;
|
||||
|
||||
[DebuggerNonUserCode] // avoid "Exception User-Unhandled" Visual Studio messages
|
||||
static public bool ValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
if (sender.GetType() != typeof(HttpWebRequest))
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
|
||||
var request = (HttpWebRequest)sender;
|
||||
var hash = certificate.GetCertHashString();
|
||||
|
||||
ICollection<string> hosts;
|
||||
|
||||
trustedCertificates.TryGetValue(hash, out hosts);
|
||||
if (hosts != null)
|
||||
{
|
||||
if (hosts.Contains(request.Host))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sslPolicyErrors != SslPolicyErrors.None)
|
||||
{
|
||||
// Throw exception with certificate details, this will cause a "Exception User-Unhandled" when running it in the Visual Studio debugger.
|
||||
// The certificate is only available inside this function, so we can't catch it at the calling method.
|
||||
throw new Exception("certificate validation failed: " + certificate.ToString());
|
||||
}
|
||||
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
}
|
||||
|
||||
static public void InitProxy(ServerConfig serverConfig)
|
||||
{
|
||||
// dispose old SocksWebProxy
|
||||
@@ -100,30 +129,7 @@ namespace Jackett.Common.Utils.Clients
|
||||
}
|
||||
|
||||
// custom handler for our own internal certificates
|
||||
ServicePointManager.ServerCertificateValidationCallback += delegate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
if (sender.GetType() != typeof(HttpWebRequest))
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
|
||||
var request = (HttpWebRequest)sender;
|
||||
var hash = certificate.GetCertHashString();
|
||||
|
||||
ICollection<string> hosts;
|
||||
|
||||
trustedCertificates.TryGetValue(hash, out hosts);
|
||||
if (hosts != null)
|
||||
{
|
||||
if (hosts.Contains(request.Host))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sslPolicyErrors != SslPolicyErrors.None)
|
||||
{
|
||||
throw new Exception("certificate validation failed: " + certificate.ToString());
|
||||
}
|
||||
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
};
|
||||
ServicePointManager.ServerCertificateValidationCallback += ValidateCertificate;
|
||||
}
|
||||
|
||||
override protected async Task<WebClientByteResult> Run(WebRequest webRequest)
|
||||
|
@@ -14,6 +14,7 @@ using Jackett.Common.Models.Config;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using NLog;
|
||||
using Jackett.Common.Helpers;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Jackett.Common.Utils.Clients
|
||||
{
|
||||
@@ -30,6 +31,34 @@ namespace Jackett.Common.Utils.Clients
|
||||
static protected string webProxyUrl;
|
||||
static protected IWebProxy webProxy;
|
||||
|
||||
[DebuggerNonUserCode] // avoid "Exception User-Unhandled" Visual Studio messages
|
||||
static public bool ValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
if (sender.GetType() != typeof(HttpWebRequest))
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
|
||||
var request = (HttpWebRequest)sender;
|
||||
var hash = certificate.GetCertHashString();
|
||||
|
||||
ICollection<string> hosts;
|
||||
|
||||
trustedCertificates.TryGetValue(hash, out hosts);
|
||||
if (hosts != null)
|
||||
{
|
||||
if (hosts.Contains(request.Host))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sslPolicyErrors != SslPolicyErrors.None)
|
||||
{
|
||||
// Throw exception with certificate details, this will cause a "Exception User-Unhandled" when running it in the Visual Studio debugger.
|
||||
// The certificate is only available inside this function, so we can't catch it at the calling method.
|
||||
throw new Exception("certificate validation failed: " + certificate.ToString());
|
||||
}
|
||||
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
}
|
||||
|
||||
static public void InitProxy(ServerConfig serverConfig)
|
||||
{
|
||||
// dispose old SocksWebProxy
|
||||
@@ -134,30 +163,7 @@ namespace Jackett.Common.Utils.Clients
|
||||
ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 | (SecurityProtocolType)768 | (SecurityProtocolType)3072;
|
||||
|
||||
// custom handler for our own internal certificates
|
||||
ServicePointManager.ServerCertificateValidationCallback += delegate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
if (sender.GetType() != typeof(HttpWebRequest))
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
|
||||
var request = (HttpWebRequest)sender;
|
||||
var hash = certificate.GetCertHashString();
|
||||
|
||||
ICollection<string> hosts;
|
||||
|
||||
trustedCertificates.TryGetValue(hash, out hosts);
|
||||
if (hosts != null)
|
||||
{
|
||||
if (hosts.Contains(request.Host))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sslPolicyErrors != SslPolicyErrors.None)
|
||||
{
|
||||
throw new Exception("certificate validation failed: " + certificate.ToString());
|
||||
}
|
||||
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
};
|
||||
ServicePointManager.ServerCertificateValidationCallback += ValidateCertificate;
|
||||
}
|
||||
|
||||
override protected async Task<WebClientByteResult> Run(WebRequest webRequest)
|
||||
|
@@ -14,6 +14,7 @@ using Jackett.Common.Models.Config;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using NLog;
|
||||
using Jackett.Common.Helpers;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Jackett.Common.Utils.Clients
|
||||
{
|
||||
@@ -24,6 +25,32 @@ namespace Jackett.Common.Utils.Clients
|
||||
static protected string webProxyUrl;
|
||||
static protected IWebProxy webProxy;
|
||||
|
||||
[DebuggerNonUserCode] // avoid "Exception User-Unhandled" Visual Studio messages
|
||||
static public bool ValidateCertificate(HttpRequestMessage request, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
{
|
||||
var hash = certificate.GetCertHashString();
|
||||
|
||||
ICollection<string> hosts;
|
||||
|
||||
trustedCertificates.TryGetValue(hash, out hosts);
|
||||
if (hosts != null)
|
||||
{
|
||||
if (hosts.Contains(request.RequestUri.Host))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sslPolicyErrors != SslPolicyErrors.None)
|
||||
{
|
||||
// Throw exception with certificate details, this will cause a "Exception User-Unhandled" when running it in the Visual Studio debugger.
|
||||
// The certificate is only available inside this function, so we can't catch it at the calling method.
|
||||
throw new Exception("certificate validation failed: " + certificate.ToString());
|
||||
}
|
||||
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
}
|
||||
}
|
||||
|
||||
static public void InitProxy(ServerConfig serverConfig)
|
||||
{
|
||||
// dispose old SocksWebProxy
|
||||
@@ -137,26 +164,7 @@ namespace Jackett.Common.Utils.Clients
|
||||
})
|
||||
{
|
||||
// custom certificate validation handler (netcore version)
|
||||
clientHandlr.ServerCertificateCustomValidationCallback = (request, certificate, chain, sslPolicyErrors) =>
|
||||
{
|
||||
var hash = certificate.GetCertHashString();
|
||||
|
||||
ICollection<string> hosts;
|
||||
|
||||
trustedCertificates.TryGetValue(hash, out hosts);
|
||||
if (hosts != null)
|
||||
{
|
||||
if (hosts.Contains(request.RequestUri.Host))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sslPolicyErrors != SslPolicyErrors.None)
|
||||
{
|
||||
throw new Exception("certificate validation failed: " + certificate.ToString());
|
||||
}
|
||||
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
};
|
||||
clientHandlr.ServerCertificateCustomValidationCallback = ValidateCertificate;
|
||||
|
||||
clearanceHandlr.InnerHandler = clientHandlr;
|
||||
using (var client = new HttpClient(clearanceHandlr))
|
||||
|
Reference in New Issue
Block a user