mirror of https://github.com/Jackett/Jackett
Fix: Redhat/NSS based libcurl not being able to access certain indexers
This commit is contained in:
parent
1606e3379f
commit
796bb70421
|
@ -118,7 +118,6 @@ namespace CurlSharp
|
||||||
private NativeMethods._CurlDebugCallback _pcbDebug;
|
private NativeMethods._CurlDebugCallback _pcbDebug;
|
||||||
private NativeMethods._CurlIoctlCallback _pcbIoctl;
|
private NativeMethods._CurlIoctlCallback _pcbIoctl;
|
||||||
private NativeMethods._CurlProgressCallback _pcbProgress;
|
private NativeMethods._CurlProgressCallback _pcbProgress;
|
||||||
private NativeMethods._CurlSslCtxCallback _pcbSslCtx;
|
|
||||||
#endif
|
#endif
|
||||||
private CurlDebugCallback _pfCurlDebug;
|
private CurlDebugCallback _pfCurlDebug;
|
||||||
private CurlHeaderCallback _pfCurlHeader;
|
private CurlHeaderCallback _pfCurlHeader;
|
||||||
|
@ -293,18 +292,6 @@ namespace CurlSharp
|
||||||
return setCurlOpt(_curlDebugData, CurlOption.DebugData);
|
return setCurlOpt(_curlDebugData, CurlOption.DebugData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntPtr _curlSslCtxData = IntPtr.Zero;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Object to pass to OnSslCtxCallback.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private CurlCode setSslCtxData(object data)
|
|
||||||
{
|
|
||||||
_curlSslCtxData = getHandle(data);
|
|
||||||
return setCurlOpt(_curlSslCtxData, CurlOption.SslCtxData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IntPtr _curlIoctlData = IntPtr.Zero;
|
private IntPtr _curlIoctlData = IntPtr.Zero;
|
||||||
|
|
||||||
|
@ -368,17 +355,6 @@ namespace CurlSharp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public object SslCtxData
|
|
||||||
{
|
|
||||||
get { return _sslContextData; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_sslContextData = value;
|
|
||||||
#if !USE_LIBCURLSHIM
|
|
||||||
setSslCtxData(value);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public object IoctlData
|
public object IoctlData
|
||||||
{
|
{
|
||||||
|
@ -538,11 +514,6 @@ namespace CurlSharp
|
||||||
set { setFunctionOptions(CurlOption.IoctlFunction, value); }
|
set { setFunctionOptions(CurlOption.IoctlFunction, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public CurlSslContextCallback SslContextFunction
|
|
||||||
{
|
|
||||||
get { return _pfCurlSslContext; }
|
|
||||||
set { setFunctionOptions(CurlOption.SslCtxFunction, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LastErrorDescription
|
public string LastErrorDescription
|
||||||
{
|
{
|
||||||
|
@ -1262,7 +1233,6 @@ namespace CurlSharp
|
||||||
freeHandle(ref _curlProgressData);
|
freeHandle(ref _curlProgressData);
|
||||||
freeHandle(ref _curlHeaderData);
|
freeHandle(ref _curlHeaderData);
|
||||||
freeHandle(ref _curlIoctlData);
|
freeHandle(ref _curlIoctlData);
|
||||||
freeHandle(ref _curlSslCtxData);
|
|
||||||
#endif
|
#endif
|
||||||
NativeMethods.curl_easy_cleanup(_pCurl);
|
NativeMethods.curl_easy_cleanup(_pCurl);
|
||||||
|
|
||||||
|
@ -1439,9 +1409,6 @@ namespace CurlSharp
|
||||||
case CurlOption.HeaderData:
|
case CurlOption.HeaderData:
|
||||||
_headerData = parameter;
|
_headerData = parameter;
|
||||||
break;
|
break;
|
||||||
case CurlOption.SslCtxData:
|
|
||||||
_sslContextData = parameter;
|
|
||||||
break;
|
|
||||||
case CurlOption.IoctlData:
|
case CurlOption.IoctlData:
|
||||||
_ioctlData = parameter;
|
_ioctlData = parameter;
|
||||||
break;
|
break;
|
||||||
|
@ -1593,14 +1560,6 @@ namespace CurlSharp
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CurlOption.SslCtxFunction:
|
|
||||||
{
|
|
||||||
var sf = pfn as CurlSslContextCallback;
|
|
||||||
if (sf == null)
|
|
||||||
return CurlCode.BadFunctionArgument;
|
|
||||||
_pfCurlSslContext = sf;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CurlOption.IoctlFunction:
|
case CurlOption.IoctlFunction:
|
||||||
{
|
{
|
||||||
|
@ -1949,7 +1908,6 @@ namespace CurlSharp
|
||||||
_pcbProgress = _curlProgressCallback;
|
_pcbProgress = _curlProgressCallback;
|
||||||
_pcbDebug = _curlDebugCallback;
|
_pcbDebug = _curlDebugCallback;
|
||||||
_pcbHeader = _curlHeaderCallback;
|
_pcbHeader = _curlHeaderCallback;
|
||||||
_pcbSslCtx = _curlSslCtxCallback;
|
|
||||||
_pcbIoctl = _curlIoctlCallback;
|
_pcbIoctl = _curlIoctlCallback;
|
||||||
|
|
||||||
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.WriteFunction, _pcbWrite),
|
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.WriteFunction, _pcbWrite),
|
||||||
|
@ -1962,8 +1920,6 @@ namespace CurlSharp
|
||||||
CurlOption.HeaderFunction);
|
CurlOption.HeaderFunction);
|
||||||
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.DebugFunction, _pcbDebug),
|
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.DebugFunction, _pcbDebug),
|
||||||
CurlOption.DebugFunction);
|
CurlOption.DebugFunction);
|
||||||
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.SslCtxFunction, _pcbSslCtx),
|
|
||||||
CurlOption.SslCtxFunction);
|
|
||||||
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.IoctlFunction, _pcbIoctl),
|
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.IoctlFunction, _pcbIoctl),
|
||||||
CurlOption.IoctlFunction);
|
CurlOption.IoctlFunction);
|
||||||
setLastError(NativeMethods.curl_easy_setopt(_pCurl, CurlOption.NoProgress, (IntPtr) 0),
|
setLastError(NativeMethods.curl_easy_setopt(_pCurl, CurlOption.NoProgress, (IntPtr) 0),
|
||||||
|
@ -1974,7 +1930,6 @@ namespace CurlSharp
|
||||||
setHeaderData(null);
|
setHeaderData(null);
|
||||||
setProgressData(null);
|
setProgressData(null);
|
||||||
setDebugData(null);
|
setDebugData(null);
|
||||||
setSslCtxData(null);
|
|
||||||
setIoctlData(null);
|
setIoctlData(null);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
<Compile Include="Enums\CurlVersionFeatureBitmask.cs" />
|
<Compile Include="Enums\CurlVersionFeatureBitmask.cs" />
|
||||||
<Compile Include="Callbacks\CurlEasyCallbacks.cs" />
|
<Compile Include="Callbacks\CurlEasyCallbacks.cs" />
|
||||||
<Compile Include="Callbacks\CurlShareCallbacks.cs" />
|
<Compile Include="Callbacks\CurlShareCallbacks.cs" />
|
||||||
|
<Compile Include="SSLFix.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
|
|
@ -1075,32 +1075,6 @@ namespace CurlSharp
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SslCipherList = 10083,
|
SslCipherList = 10083,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Object reference to pass to the ssl context delegate set by the option
|
|
||||||
/// <c>SslCtxFunction</c>, this is the pointer you'll get as the
|
|
||||||
/// second parameter, otherwise <c>null</c>. (Added in 7.11.0)
|
|
||||||
/// </summary>
|
|
||||||
SslCtxData = 10109,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reference to an <see cref="CurlEasy.CurlSslContextCallback" /> delegate.
|
|
||||||
/// This delegate gets called by libcurl just before the initialization of
|
|
||||||
/// an Ssl connection after having processed all other Ssl related options
|
|
||||||
/// to give a last chance to an application to modify the behaviour of
|
|
||||||
/// openssl's ssl initialization. The <see cref="CurlSslContext" /> parameter
|
|
||||||
/// wraps a pointer to an openssl SSL_CTX. If an error is returned no attempt
|
|
||||||
/// to establish a connection is made and the perform operation will return
|
|
||||||
/// the error code from this callback function. Set the parm argument with
|
|
||||||
/// the <c>SslCtxData</c> option. This option was introduced
|
|
||||||
/// in 7.11.0.
|
|
||||||
/// <note>
|
|
||||||
/// To use this properly, a non-trivial amount of knowledge of the openssl
|
|
||||||
/// libraries is necessary. Using this function allows for example to use
|
|
||||||
/// openssl callbacks to add additional validation code for certificates,
|
|
||||||
/// and even to change the actual URI of an HTTPS request.
|
|
||||||
/// </note>
|
|
||||||
/// </summary>
|
|
||||||
SslCtxFunction = 20108,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pass an <c>int</c>. Set if we should verify the common name from the
|
/// Pass an <c>int</c>. Set if we should verify the common name from the
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CurlSharp
|
||||||
|
{
|
||||||
|
public class SSLFix
|
||||||
|
{
|
||||||
|
public const string CipherList = "rsa_aes_128_sha,ecdhe_rsa_aes_256_sha,ecdhe_ecdsa_aes_128_sha";
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ namespace Jackett.Console
|
||||||
[Option('t', "Tracing", HelpText = "Enable tracing")]
|
[Option('t', "Tracing", HelpText = "Enable tracing")]
|
||||||
public bool Tracing { get; set; }
|
public bool Tracing { get; set; }
|
||||||
|
|
||||||
[Option('c', "UseClient", HelpText = "Override web client selection. Automatic(Default)/libcurl/safecurl/httpclient ")]
|
[Option('c', "UseClient", HelpText = "Override web client selection. [automatic(Default)/libcurl/safecurl/httpclient]")]
|
||||||
public string Client { get; set; }
|
public string Client { get; set; }
|
||||||
|
|
||||||
[Option('s', "Start", HelpText = "Start the Jacket Windows service (Must be admin)")]
|
[Option('s', "Start", HelpText = "Start the Jacket Windows service (Must be admin)")]
|
||||||
|
@ -33,7 +33,7 @@ namespace Jackett.Console
|
||||||
[Option('k', "Stop", HelpText = "Stop the Jacket Windows service (Must be admin)")]
|
[Option('k', "Stop", HelpText = "Stop the Jacket Windows service (Must be admin)")]
|
||||||
public bool StopService { get; set; }
|
public bool StopService { get; set; }
|
||||||
|
|
||||||
[Option('x', "ListenPublic", HelpText = "Listen publicly")]
|
[Option('x', "ListenPublic", HelpText = "Listen publicly [true/false]")]
|
||||||
public bool? ListenPublic { get; set; }
|
public bool? ListenPublic { get; set; }
|
||||||
|
|
||||||
[Option('h', "Help", HelpText = "Show Help")]
|
[Option('h', "Help", HelpText = "Show Help")]
|
||||||
|
@ -47,5 +47,8 @@ namespace Jackett.Console
|
||||||
|
|
||||||
[Option('m', "MigrateSettings", HelpText = "Migrate settings manually (Must be admin on Windows)")]
|
[Option('m', "MigrateSettings", HelpText = "Migrate settings manually (Must be admin on Windows)")]
|
||||||
public bool MigrateSettings { get; set; }
|
public bool MigrateSettings { get; set; }
|
||||||
|
|
||||||
|
[Option('f', "SSLFix", HelpText = "Linux Libcurl NSS Missing ECC Ciphers workaround (Use if you can't access some trackers) [true/false].")]
|
||||||
|
public bool? SSLFix { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,11 +35,11 @@ namespace JackettConsole
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ====== Options ===== */
|
/* ====== Options ===== */
|
||||||
|
|
||||||
|
// SSL Fix
|
||||||
|
Startup.DoSSLFix = options.SSLFix;
|
||||||
|
|
||||||
// Use curl
|
// Use curl
|
||||||
if (options.Client!=null)
|
if (options.Client!=null)
|
||||||
Startup.ClientOverride = options.Client.ToLowerInvariant();
|
Startup.ClientOverride = options.Client.ToLowerInvariant();
|
||||||
|
@ -60,6 +60,10 @@ namespace JackettConsole
|
||||||
if (options.Tracing)
|
if (options.Tracing)
|
||||||
Engine.Logger.Info("Tracing enabled.");
|
Engine.Logger.Info("Tracing enabled.");
|
||||||
|
|
||||||
|
if (options.SSLFix == true)
|
||||||
|
Engine.Logger.Info("SSL ECC workaround enabled.");
|
||||||
|
else if (options.SSLFix == false)
|
||||||
|
Engine.Logger.Info("SSL ECC workaround has been disabled.");
|
||||||
/* ====== Actions ===== */
|
/* ====== Actions ===== */
|
||||||
|
|
||||||
// Install service
|
// Install service
|
||||||
|
|
|
@ -5,6 +5,7 @@ using Jackett.Services;
|
||||||
using Jackett.Utils;
|
using Jackett.Utils;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -34,8 +35,9 @@ namespace Jackett.Controllers
|
||||||
private ISecuityService securityService;
|
private ISecuityService securityService;
|
||||||
private IProcessService processService;
|
private IProcessService processService;
|
||||||
private ICacheService cacheService;
|
private ICacheService cacheService;
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
public AdminController(IConfigurationService config, IIndexerManagerService i, IServerService ss, ISecuityService s, IProcessService p, ICacheService c)
|
public AdminController(IConfigurationService config, IIndexerManagerService i, IServerService ss, ISecuityService s, IProcessService p, ICacheService c, Logger l)
|
||||||
{
|
{
|
||||||
this.config = config;
|
this.config = config;
|
||||||
indexerService = i;
|
indexerService = i;
|
||||||
|
@ -43,6 +45,7 @@ namespace Jackett.Controllers
|
||||||
securityService = s;
|
securityService = s;
|
||||||
processService = p;
|
processService = p;
|
||||||
cacheService = c;
|
cacheService = c;
|
||||||
|
logger = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<JToken> ReadPostDataJson()
|
private async Task<JToken> ReadPostDataJson()
|
||||||
|
@ -130,6 +133,7 @@ namespace Jackett.Controllers
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
logger.Error(ex, "Exception in SetAdminPassword");
|
||||||
jsonReply["result"] = "error";
|
jsonReply["result"] = "error";
|
||||||
jsonReply["error"] = ex.Message;
|
jsonReply["error"] = ex.Message;
|
||||||
}
|
}
|
||||||
|
@ -153,6 +157,7 @@ namespace Jackett.Controllers
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
logger.Error(ex, "Exception in GetConfigForm");
|
||||||
jsonReply["result"] = "error";
|
jsonReply["result"] = "error";
|
||||||
jsonReply["error"] = ex.Message;
|
jsonReply["error"] = ex.Message;
|
||||||
}
|
}
|
||||||
|
@ -185,6 +190,9 @@ namespace Jackett.Controllers
|
||||||
if (ex is ExceptionWithConfigData)
|
if (ex is ExceptionWithConfigData)
|
||||||
{
|
{
|
||||||
jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson();
|
jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
logger.Error(ex, "Exception in Configure");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Json(jsonReply);
|
return Json(jsonReply);
|
||||||
|
@ -214,6 +222,7 @@ namespace Jackett.Controllers
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
logger.Error(ex, "Exception in get_indexers");
|
||||||
jsonReply["result"] = "error";
|
jsonReply["result"] = "error";
|
||||||
jsonReply["error"] = ex.Message;
|
jsonReply["error"] = ex.Message;
|
||||||
}
|
}
|
||||||
|
@ -235,6 +244,7 @@ namespace Jackett.Controllers
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
logger.Error(ex, "Exception in test_indexer");
|
||||||
jsonReply["result"] = "error";
|
jsonReply["result"] = "error";
|
||||||
jsonReply["error"] = ex.Message;
|
jsonReply["error"] = ex.Message;
|
||||||
}
|
}
|
||||||
|
@ -254,6 +264,7 @@ namespace Jackett.Controllers
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
logger.Error(ex, "Exception in delete_indexer");
|
||||||
jsonReply["result"] = "error";
|
jsonReply["result"] = "error";
|
||||||
jsonReply["error"] = ex.Message;
|
jsonReply["error"] = ex.Message;
|
||||||
}
|
}
|
||||||
|
@ -276,14 +287,9 @@ namespace Jackett.Controllers
|
||||||
jsonReply["config"] = cfg;
|
jsonReply["config"] = cfg;
|
||||||
jsonReply["app_version"] = config.GetVersion();
|
jsonReply["app_version"] = config.GetVersion();
|
||||||
jsonReply["result"] = "success";
|
jsonReply["result"] = "success";
|
||||||
}
|
}catch (Exception ex)
|
||||||
catch (CustomException ex)
|
|
||||||
{
|
|
||||||
jsonReply["result"] = "error";
|
|
||||||
jsonReply["error"] = ex.Message;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
{
|
||||||
|
logger.Error(ex, "Exception in get_jackett_config");
|
||||||
jsonReply["result"] = "error";
|
jsonReply["result"] = "error";
|
||||||
jsonReply["error"] = ex.Message;
|
jsonReply["error"] = ex.Message;
|
||||||
}
|
}
|
||||||
|
@ -357,6 +363,7 @@ namespace Jackett.Controllers
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
logger.Error(ex, "Exception in set_port");
|
||||||
jsonReply["result"] = "error";
|
jsonReply["result"] = "error";
|
||||||
jsonReply["error"] = ex.Message;
|
jsonReply["error"] = ex.Message;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using Jackett.Utils;
|
using Jackett.Utils;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Jackett
|
namespace Jackett
|
||||||
{
|
{
|
||||||
|
@ -18,15 +19,10 @@ namespace Jackett
|
||||||
|
|
||||||
public class CurlRequest
|
public class CurlRequest
|
||||||
{
|
{
|
||||||
|
|
||||||
public string Url { get; private set; }
|
public string Url { get; private set; }
|
||||||
|
|
||||||
public string Cookies { get; private set; }
|
public string Cookies { get; private set; }
|
||||||
|
|
||||||
public string Referer { get; private set; }
|
public string Referer { get; private set; }
|
||||||
|
|
||||||
public HttpMethod Method { get; private set; }
|
public HttpMethod Method { get; private set; }
|
||||||
|
|
||||||
public Dictionary<string, string> PostData { get; set; }
|
public Dictionary<string, string> PostData { get; set; }
|
||||||
|
|
||||||
public CurlRequest(HttpMethod method, string url, string cookies = null, string referer = null)
|
public CurlRequest(HttpMethod method, string url, string cookies = null, string referer = null)
|
||||||
|
@ -40,49 +36,17 @@ namespace Jackett
|
||||||
|
|
||||||
public class CurlResponse
|
public class CurlResponse
|
||||||
{
|
{
|
||||||
public Dictionary<string, string> Headers { get; private set; }
|
|
||||||
public List<string[]> HeaderList { get; private set; }
|
public List<string[]> HeaderList { get; private set; }
|
||||||
public byte[] Content { get; private set; }
|
public byte[] Content { get; private set; }
|
||||||
public HttpStatusCode Status { get; private set;}
|
public HttpStatusCode Status { get; private set;}
|
||||||
public Dictionary<string, string> Cookies { get; private set; }
|
public string Cookies { set; get; }
|
||||||
public List<string> CookiesFlat { get { return Cookies.Select(c => c.Key + "=" + c.Value).ToList(); } }
|
|
||||||
public string CookieHeader { get { return string.Join("; ", CookiesFlat); } }
|
|
||||||
|
|
||||||
public CurlResponse(List<string[]> headers, byte[] content, HttpStatusCode s)
|
public CurlResponse(List<string[]> headers, byte[] content, HttpStatusCode s, string cookies)
|
||||||
{
|
{
|
||||||
Headers = new Dictionary<string, string>();
|
|
||||||
Cookies = new Dictionary<string, string>();
|
|
||||||
HeaderList = headers;
|
HeaderList = headers;
|
||||||
Content = content;
|
Content = content;
|
||||||
Status = s;
|
Status = s;
|
||||||
foreach (var h in headers)
|
Cookies = cookies;
|
||||||
{
|
|
||||||
Headers[h[0]] = h[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddCookiesFromHeaderValue(string cookieHeaderValue)
|
|
||||||
{
|
|
||||||
var rawCookies = cookieHeaderValue.Split(';');
|
|
||||||
foreach (var rawCookie in rawCookies)
|
|
||||||
{
|
|
||||||
var parts = rawCookie.Split(new char[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (parts.Length == 1)
|
|
||||||
Cookies[rawCookie.Trim()] = string.Empty;
|
|
||||||
else
|
|
||||||
Cookies[parts[0].Trim()] = parts[1].Trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddCookiesFromHeaders(List<string[]> headers)
|
|
||||||
{
|
|
||||||
foreach (var h in headers)
|
|
||||||
{
|
|
||||||
if (h[0] == "set-cookie")
|
|
||||||
{
|
|
||||||
AddCookiesFromHeaderValue(h[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,36 +65,14 @@ namespace Jackett
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<CurlResponse> FollowRedirect(string url, CurlResponse response)
|
|
||||||
{
|
|
||||||
var uri = new Uri(url);
|
|
||||||
string redirect;
|
|
||||||
if (response.Headers.TryGetValue("location", out redirect))
|
|
||||||
{
|
|
||||||
string cookie = response.CookieHeader;
|
|
||||||
if (!redirect.StartsWith("http://") && !redirect.StartsWith("https://"))
|
|
||||||
{
|
|
||||||
if (redirect.StartsWith("/"))
|
|
||||||
redirect = string.Format("{0}://{1}{2}", uri.Scheme, uri.Host, redirect);
|
|
||||||
else
|
|
||||||
redirect = string.Format("{0}://{1}/{2}", uri.Scheme, uri.Host, redirect);
|
|
||||||
}
|
|
||||||
var newRedirect = await GetAsync(redirect, cookie);
|
|
||||||
foreach (var c in response.Cookies)
|
|
||||||
newRedirect.Cookies[c.Key] = c.Value;
|
|
||||||
newRedirect.AddCookiesFromHeaders(response.HeaderList);
|
|
||||||
return newRedirect;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static async Task<CurlResponse> PerformCurlAsync(CurlRequest curlRequest)
|
public static async Task<CurlResponse> PerformCurlAsync(CurlRequest curlRequest)
|
||||||
{
|
{
|
||||||
return await Task.Run(() => PerformCurl(curlRequest));
|
return await Task.Run(() => PerformCurl(curlRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public delegate void ErrorMessage(string s);
|
||||||
|
public static ErrorMessage OnErrorMessage;
|
||||||
|
|
||||||
public static CurlResponse PerformCurl(CurlRequest curlRequest)
|
public static CurlResponse PerformCurl(CurlRequest curlRequest)
|
||||||
{
|
{
|
||||||
lock (instance)
|
lock (instance)
|
||||||
|
@ -145,11 +87,13 @@ namespace Jackett
|
||||||
easy.UserAgent = BrowserUtil.ChromeUserAgent;
|
easy.UserAgent = BrowserUtil.ChromeUserAgent;
|
||||||
easy.FollowLocation = false;
|
easy.FollowLocation = false;
|
||||||
easy.ConnectTimeout = 20;
|
easy.ConnectTimeout = 20;
|
||||||
|
|
||||||
easy.WriteFunction = (byte[] buf, int size, int nmemb, object data) =>
|
easy.WriteFunction = (byte[] buf, int size, int nmemb, object data) =>
|
||||||
{
|
{
|
||||||
contentBuffers.Add(buf);
|
contentBuffers.Add(buf);
|
||||||
return size * nmemb;
|
return size * nmemb;
|
||||||
};
|
};
|
||||||
|
|
||||||
easy.HeaderFunction = (byte[] buf, int size, int nmemb, object extraData) =>
|
easy.HeaderFunction = (byte[] buf, int size, int nmemb, object extraData) =>
|
||||||
{
|
{
|
||||||
headerBuffers.Add(buf);
|
headerBuffers.Add(buf);
|
||||||
|
@ -170,7 +114,25 @@ namespace Jackett
|
||||||
easy.PostFieldSize = Encoding.UTF8.GetByteCount(postString);
|
easy.PostFieldSize = Encoding.UTF8.GetByteCount(postString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Startup.DoSSLFix == true)
|
||||||
|
{
|
||||||
|
// http://stackoverflow.com/questions/31107851/how-to-fix-curl-35-cannot-communicate-securely-with-peer-no-common-encryptio
|
||||||
|
// https://git.fedorahosted.org/cgit/mod_nss.git/plain/docs/mod_nss.html
|
||||||
|
easy.SslCipherList = SSLFix.CipherList;
|
||||||
|
easy.FreshConnect = true;
|
||||||
|
easy.ForbidReuse = true;
|
||||||
|
}
|
||||||
|
|
||||||
easy.Perform();
|
easy.Perform();
|
||||||
|
|
||||||
|
if(easy.LastErrorCode != CurlCode.Ok)
|
||||||
|
{
|
||||||
|
var message = "Error " + easy.LastErrorCode.ToString() + " " + easy.LastErrorDescription;
|
||||||
|
if (null != OnErrorMessage)
|
||||||
|
OnErrorMessage(message);
|
||||||
|
else
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var headerBytes = Combine(headerBuffers.ToArray());
|
var headerBytes = Combine(headerBuffers.ToArray());
|
||||||
|
@ -179,10 +141,14 @@ namespace Jackett
|
||||||
var headers = new List<string[]>();
|
var headers = new List<string[]>();
|
||||||
var headerCount = 0;
|
var headerCount = 0;
|
||||||
HttpStatusCode status = HttpStatusCode.InternalServerError;
|
HttpStatusCode status = HttpStatusCode.InternalServerError;
|
||||||
|
var cookieBuilder = new StringBuilder();
|
||||||
foreach (var headerPart in headerParts)
|
foreach (var headerPart in headerParts)
|
||||||
{
|
{
|
||||||
if (headerCount == 0)
|
if (headerCount == 0)
|
||||||
{
|
{
|
||||||
|
var split = headerPart.Split(' ');
|
||||||
|
if (split.Length < 2)
|
||||||
|
throw new Exception("HTTP Header missing");
|
||||||
var responseCode = int.Parse(headerPart.Split(' ')[1]);
|
var responseCode = int.Parse(headerPart.Split(' ')[1]);
|
||||||
status = (HttpStatusCode)responseCode;
|
status = (HttpStatusCode)responseCode;
|
||||||
}
|
}
|
||||||
|
@ -191,7 +157,17 @@ namespace Jackett
|
||||||
var keyVal = headerPart.Split(new char[] { ':' }, 2);
|
var keyVal = headerPart.Split(new char[] { ':' }, 2);
|
||||||
if (keyVal.Length > 1)
|
if (keyVal.Length > 1)
|
||||||
{
|
{
|
||||||
headers.Add(new[] { keyVal[0].ToLower().Trim(), keyVal[1].Trim() });
|
var key = keyVal[0].ToLower().Trim();
|
||||||
|
var value = keyVal[1].Trim();
|
||||||
|
|
||||||
|
if (key == "set-cookie")
|
||||||
|
{
|
||||||
|
cookieBuilder.AppendFormat("{0} ", value.Substring(0, value.IndexOf(';') + 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
headers.Add(new[] { key, value });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,12 +175,7 @@ namespace Jackett
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentBytes = Combine(contentBuffers.ToArray());
|
var contentBytes = Combine(contentBuffers.ToArray());
|
||||||
|
var curlResponse = new CurlResponse(headers, contentBytes, status, cookieBuilder.ToString().TrimEnd());
|
||||||
var curlResponse = new CurlResponse(headers, contentBytes, status);
|
|
||||||
if (!string.IsNullOrEmpty(curlRequest.Cookies))
|
|
||||||
curlResponse.AddCookiesFromHeaderValue(curlRequest.Cookies);
|
|
||||||
curlResponse.AddCookiesFromHeaders(headers);
|
|
||||||
|
|
||||||
return curlResponse;
|
return curlResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ namespace Jackett
|
||||||
logFile.FileName = Path.Combine(ConfigurationService.GetAppDataFolderStatic(), "log.txt");
|
logFile.FileName = Path.Combine(ConfigurationService.GetAppDataFolderStatic(), "log.txt");
|
||||||
logFile.ArchiveFileName = "log.{#####}.txt";
|
logFile.ArchiveFileName = "log.{#####}.txt";
|
||||||
logFile.ArchiveAboveSize = 500000;
|
logFile.ArchiveAboveSize = 500000;
|
||||||
logFile.MaxArchiveFiles = 1;
|
logFile.MaxArchiveFiles = 5;
|
||||||
logFile.KeepFileOpen = false;
|
logFile.KeepFileOpen = false;
|
||||||
logFile.ArchiveNumbering = ArchiveNumberingMode.DateAndSequence;
|
logFile.ArchiveNumbering = ArchiveNumberingMode.DateAndSequence;
|
||||||
var logFileRule = new LoggingRule("*", logLevel, logFile);
|
var logFileRule = new LoggingRule("*", logLevel, logFile);
|
||||||
|
|
|
@ -18,11 +18,4 @@ namespace Jackett
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CustomException : Exception
|
|
||||||
{
|
|
||||||
public CustomException(string message)
|
|
||||||
: base(message)
|
|
||||||
{ }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,10 @@ namespace Jackett.Indexers
|
||||||
{ "submit", "come on in" }
|
{ "submit", "come on in" }
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, SiteLink, LoginUrl);
|
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||||
ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("nav_profile"), () =>
|
|
||||||
|
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, SiteLink, LoginUrl);
|
||||||
|
ConfigureIfOK(result.Cookies + " " + loginPage.Cookies, result.Content != null && result.Content.Contains("nav_profile"), () =>
|
||||||
{
|
{
|
||||||
CQ dom = result.Content;
|
CQ dom = result.Content;
|
||||||
var messageEl = dom["#login_box_desc"];
|
var messageEl = dom["#login_box_desc"];
|
||||||
|
|
|
@ -120,15 +120,12 @@ namespace Jackett.Indexers
|
||||||
// 15 results per page - really don't want to call the server twice but only 15 results per page is a bit crap!
|
// 15 results per page - really don't want to call the server twice but only 15 results per page is a bit crap!
|
||||||
await ProcessPage(releases, searchUrl);
|
await ProcessPage(releases, searchUrl);
|
||||||
await ProcessPage(releases, searchUrl + "&page=1");
|
await ProcessPage(releases, searchUrl + "&page=1");
|
||||||
|
|
||||||
return releases;
|
return releases;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProcessPage(List<ReleaseInfo> releases, string searchUrl)
|
private async Task ProcessPage(List<ReleaseInfo> releases, string searchUrl)
|
||||||
{
|
{
|
||||||
|
|
||||||
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
|
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
|
||||||
|
|
||||||
var results = response.Content;
|
var results = response.Content;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,6 +40,12 @@ namespace Jackett
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool? DoSSLFix
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
public void Configuration(IAppBuilder appBuilder)
|
public void Configuration(IAppBuilder appBuilder)
|
||||||
{
|
{
|
||||||
// Configure Web API for self-host.
|
// Configure Web API for self-host.
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace Jackett.Utils.Clients
|
||||||
{
|
{
|
||||||
logger.Debug(string.Format("WindowsWebClient:GetBytes(Url:{0})", request.Url));
|
logger.Debug(string.Format("WindowsWebClient:GetBytes(Url:{0})", request.Url));
|
||||||
var result = await Run(request);
|
var result = await Run(request);
|
||||||
logger.Debug(string.Format("WindowsWebClient: Returning", result.Status));
|
logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1} bytes", result.Status, (result.Content == null ? "<NULL>" : result.Content.Length.ToString())));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ namespace Jackett.Utils.Clients
|
||||||
{
|
{
|
||||||
logger.Debug(string.Format("WindowsWebClient:GetString(Url:{0})", request.Url));
|
logger.Debug(string.Format("WindowsWebClient:GetString(Url:{0})", request.Url));
|
||||||
var result = await Run(request);
|
var result = await Run(request);
|
||||||
logger.Debug(string.Format("WindowsWebClient: Returning", result.Status));
|
logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1}", result.Status, (result.Content == null ? "<NULL>" : Encoding.UTF8.GetString(result.Content))));
|
||||||
return Mapper.Map<WebClientStringResult>(result);
|
return Mapper.Map<WebClientStringResult>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace Jackett.Utils.Clients
|
||||||
{
|
{
|
||||||
logger.Debug(string.Format("UnixLibCurlWebClient:GetBytes(Url:{0})", request.Url));
|
logger.Debug(string.Format("UnixLibCurlWebClient:GetBytes(Url:{0})", request.Url));
|
||||||
var result = await Run(request);
|
var result = await Run(request);
|
||||||
logger.Debug(string.Format("UnixLibCurlWebClient: Returning", result.Status));
|
logger.Debug(string.Format("UnixLibCurlWebClient:GetBytes Returning {0} => {1} bytes", result.Status, (result.Content==null?"<NULL>":result.Content.Length.ToString())));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,14 +35,35 @@ namespace Jackett.Utils.Clients
|
||||||
{
|
{
|
||||||
logger.Debug(string.Format("UnixLibCurlWebClient:GetString(Url:{0})", request.Url));
|
logger.Debug(string.Format("UnixLibCurlWebClient:GetString(Url:{0})", request.Url));
|
||||||
var result = await Run(request);
|
var result = await Run(request);
|
||||||
logger.Debug(string.Format("UnixLibCurlWebClient: Returning", result.Status));
|
logger.Debug(string.Format("UnixLibCurlWebClient:GetString Returning {0} => {1}", result.Status, (result.Content== null?"<NULL>": Encoding.UTF8.GetString(result.Content))));
|
||||||
return Mapper.Map<WebClientStringResult>(result);
|
return Mapper.Map<WebClientStringResult>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
Engine.Logger.Info("LibCurl init " + Curl.GlobalInit(CurlInitFlag.All).ToString());
|
Engine.Logger.Info("LibCurl init " + Curl.GlobalInit(CurlInitFlag.All).ToString());
|
||||||
Engine.Logger.Info("LibCurl version " + Curl.Version);
|
CurlHelper.OnErrorMessage += (msg) =>
|
||||||
|
{
|
||||||
|
Engine.Logger.Error(msg);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
Engine.Logger.Warn("Libcurl failed to initalize. Did you install it?");
|
||||||
|
Engine.Logger.Warn("Debian: apt-get install libcurl4-openssl-dev");
|
||||||
|
Engine.Logger.Warn("Redhat: yum install libcurl-devel");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
var version = Curl.Version;
|
||||||
|
Engine.Logger.Info("LibCurl version " + version);
|
||||||
|
|
||||||
|
if (!Startup.DoSSLFix.HasValue && version.IndexOf("NSS")>-1)
|
||||||
|
{
|
||||||
|
Engine.Logger.Info("NSS Detected SSL ECC workaround enabled.");
|
||||||
|
Startup.DoSSLFix = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<WebClientByteResult> Run(WebRequest request)
|
private async Task<WebClientByteResult> Run(WebRequest request)
|
||||||
|
@ -54,23 +75,30 @@ namespace Jackett.Utils.Clients
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (request.PostData != null && request.PostData.Count > 0)
|
||||||
|
{
|
||||||
|
logger.Debug("UnixLibCurlWebClient: Posting " + new FormUrlEncodedContent(request.PostData).ReadAsStringAsync().Result);
|
||||||
|
}
|
||||||
|
|
||||||
response = await CurlHelper.PostAsync(request.Url, request.PostData, request.Cookies, request.Referer);
|
response = await CurlHelper.PostAsync(request.Url, request.PostData, request.Cookies, request.Referer);
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new WebClientByteResult()
|
var result = new WebClientByteResult()
|
||||||
{
|
{
|
||||||
Content = response.Content,
|
Content = response.Content,
|
||||||
Cookies = response.CookieHeader,
|
Cookies = response.Cookies,
|
||||||
Status = response.Status
|
Status = response.Status
|
||||||
};
|
};
|
||||||
|
|
||||||
if (response.Headers != null)
|
if (response.HeaderList != null)
|
||||||
{
|
{
|
||||||
foreach (var header in response.Headers)
|
foreach (var header in response.HeaderList)
|
||||||
{
|
{
|
||||||
if (string.Equals(header.Key, "location", StringComparison.InvariantCultureIgnoreCase) && header.Value != null)
|
switch (header[0].ToLowerInvariant())
|
||||||
{
|
{
|
||||||
result.RedirectingTo = header.Value;
|
case "location":
|
||||||
|
result.RedirectingTo = header[1];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using CurlSharp;
|
||||||
using Jackett.Models;
|
using Jackett.Models;
|
||||||
using Jackett.Services;
|
using Jackett.Services;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
@ -32,7 +33,7 @@ namespace Jackett.Utils.Clients
|
||||||
{
|
{
|
||||||
logger.Debug(string.Format("UnixSafeCurlWebClient:GetBytes(Url:{0})", request.Url));
|
logger.Debug(string.Format("UnixSafeCurlWebClient:GetBytes(Url:{0})", request.Url));
|
||||||
var result = await Run(request);
|
var result = await Run(request);
|
||||||
logger.Debug(string.Format("UnixSafeCurlWebClient: Returning", result.Status));
|
logger.Debug(string.Format("UnixSafeCurlWebClient: Returning {0} => {1} bytes", result.Status, (result.Content == null ? "<NULL>" : result.Content.Length.ToString())));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ namespace Jackett.Utils.Clients
|
||||||
{
|
{
|
||||||
logger.Debug(string.Format("UnixSafeCurlWebClient:GetString(Url:{0})", request.Url));
|
logger.Debug(string.Format("UnixSafeCurlWebClient:GetString(Url:{0})", request.Url));
|
||||||
var result = await Run(request);
|
var result = await Run(request);
|
||||||
logger.Debug(string.Format("UnixSafeCurlWebClient: Returning", result.Status));
|
logger.Debug(string.Format("UnixSafeCurlWebClient: Returning {0} => {1}", result.Status, (result.Content == null ? "<NULL>" : Encoding.UTF8.GetString(result.Content))));
|
||||||
return Mapper.Map<WebClientStringResult>(result);
|
return Mapper.Map<WebClientStringResult>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +70,13 @@ namespace Jackett.Utils.Clients
|
||||||
var tempFile = Path.GetTempFileName();
|
var tempFile = Path.GetTempFileName();
|
||||||
args.AppendFormat("--output \"{0}\" ", tempFile);
|
args.AppendFormat("--output \"{0}\" ", tempFile);
|
||||||
|
|
||||||
|
if (Startup.DoSSLFix == true)
|
||||||
|
{
|
||||||
|
// http://stackoverflow.com/questions/31107851/how-to-fix-curl-35-cannot-communicate-securely-with-peer-no-common-encryptio
|
||||||
|
// https://git.fedorahosted.org/cgit/mod_nss.git/plain/docs/mod_nss.html
|
||||||
|
args.Append("--cipher " + SSLFix.CipherList);
|
||||||
|
}
|
||||||
|
|
||||||
string stdout = null;
|
string stdout = null;
|
||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue