Fix: Redhat/NSS based libcurl not being able to access certain indexers

This commit is contained in:
KZ 2015-07-31 20:27:59 +01:00
parent 1606e3379f
commit 796bb70421
16 changed files with 144 additions and 182 deletions

View File

@ -118,7 +118,6 @@ namespace CurlSharp
private NativeMethods._CurlDebugCallback _pcbDebug;
private NativeMethods._CurlIoctlCallback _pcbIoctl;
private NativeMethods._CurlProgressCallback _pcbProgress;
private NativeMethods._CurlSslCtxCallback _pcbSslCtx;
#endif
private CurlDebugCallback _pfCurlDebug;
private CurlHeaderCallback _pfCurlHeader;
@ -293,18 +292,6 @@ namespace CurlSharp
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;
@ -368,17 +355,6 @@ namespace CurlSharp
}
}
public object SslCtxData
{
get { return _sslContextData; }
set
{
_sslContextData = value;
#if !USE_LIBCURLSHIM
setSslCtxData(value);
#endif
}
}
public object IoctlData
{
@ -538,11 +514,6 @@ namespace CurlSharp
set { setFunctionOptions(CurlOption.IoctlFunction, value); }
}
public CurlSslContextCallback SslContextFunction
{
get { return _pfCurlSslContext; }
set { setFunctionOptions(CurlOption.SslCtxFunction, value); }
}
public string LastErrorDescription
{
@ -1262,7 +1233,6 @@ namespace CurlSharp
freeHandle(ref _curlProgressData);
freeHandle(ref _curlHeaderData);
freeHandle(ref _curlIoctlData);
freeHandle(ref _curlSslCtxData);
#endif
NativeMethods.curl_easy_cleanup(_pCurl);
@ -1439,9 +1409,6 @@ namespace CurlSharp
case CurlOption.HeaderData:
_headerData = parameter;
break;
case CurlOption.SslCtxData:
_sslContextData = parameter;
break;
case CurlOption.IoctlData:
_ioctlData = parameter;
break;
@ -1593,14 +1560,6 @@ namespace CurlSharp
break;
}
case CurlOption.SslCtxFunction:
{
var sf = pfn as CurlSslContextCallback;
if (sf == null)
return CurlCode.BadFunctionArgument;
_pfCurlSslContext = sf;
break;
}
case CurlOption.IoctlFunction:
{
@ -1949,7 +1908,6 @@ namespace CurlSharp
_pcbProgress = _curlProgressCallback;
_pcbDebug = _curlDebugCallback;
_pcbHeader = _curlHeaderCallback;
_pcbSslCtx = _curlSslCtxCallback;
_pcbIoctl = _curlIoctlCallback;
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.WriteFunction, _pcbWrite),
@ -1962,8 +1920,6 @@ namespace CurlSharp
CurlOption.HeaderFunction);
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.DebugFunction, _pcbDebug),
CurlOption.DebugFunction);
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.SslCtxFunction, _pcbSslCtx),
CurlOption.SslCtxFunction);
setLastError(NativeMethods.curl_easy_setopt_cb(_pCurl, CurlOption.IoctlFunction, _pcbIoctl),
CurlOption.IoctlFunction);
setLastError(NativeMethods.curl_easy_setopt(_pCurl, CurlOption.NoProgress, (IntPtr) 0),
@ -1974,7 +1930,6 @@ namespace CurlSharp
setHeaderData(null);
setProgressData(null);
setDebugData(null);
setSslCtxData(null);
setIoctlData(null);
#endif
}

View File

@ -78,6 +78,7 @@
<Compile Include="Enums\CurlVersionFeatureBitmask.cs" />
<Compile Include="Callbacks\CurlEasyCallbacks.cs" />
<Compile Include="Callbacks\CurlShareCallbacks.cs" />
<Compile Include="SSLFix.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -1075,32 +1075,6 @@ namespace CurlSharp
/// </summary>
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>
/// Pass an <c>int</c>. Set if we should verify the common name from the

13
src/CurlSharp/SSLFix.cs Normal file
View File

@ -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";
}
}

View File

@ -24,7 +24,7 @@ namespace Jackett.Console
[Option('t', "Tracing", HelpText = "Enable tracing")]
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; }
[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)")]
public bool StopService { get; set; }
[Option('x', "ListenPublic", HelpText = "Listen publicly")]
[Option('x', "ListenPublic", HelpText = "Listen publicly [true/false]")]
public bool? ListenPublic { get; set; }
[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)")]
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; }
}
}

View File

@ -35,11 +35,11 @@ namespace JackettConsole
}
else
{
/* ====== Options ===== */
// SSL Fix
Startup.DoSSLFix = options.SSLFix;
// Use curl
if (options.Client!=null)
Startup.ClientOverride = options.Client.ToLowerInvariant();
@ -60,6 +60,10 @@ namespace JackettConsole
if (options.Tracing)
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 ===== */
// Install service

View File

@ -5,6 +5,7 @@ using Jackett.Services;
using Jackett.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using System;
using System.Collections.Generic;
using System.IO;
@ -34,8 +35,9 @@ namespace Jackett.Controllers
private ISecuityService securityService;
private IProcessService processService;
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;
indexerService = i;
@ -43,6 +45,7 @@ namespace Jackett.Controllers
securityService = s;
processService = p;
cacheService = c;
logger = l;
}
private async Task<JToken> ReadPostDataJson()
@ -130,6 +133,7 @@ namespace Jackett.Controllers
}
catch (Exception ex)
{
logger.Error(ex, "Exception in SetAdminPassword");
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
@ -153,6 +157,7 @@ namespace Jackett.Controllers
}
catch (Exception ex)
{
logger.Error(ex, "Exception in GetConfigForm");
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
@ -185,6 +190,9 @@ namespace Jackett.Controllers
if (ex is ExceptionWithConfigData)
{
jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson();
} else
{
logger.Error(ex, "Exception in Configure");
}
}
return Json(jsonReply);
@ -214,6 +222,7 @@ namespace Jackett.Controllers
}
catch (Exception ex)
{
logger.Error(ex, "Exception in get_indexers");
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
@ -235,6 +244,7 @@ namespace Jackett.Controllers
}
catch (Exception ex)
{
logger.Error(ex, "Exception in test_indexer");
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
@ -254,6 +264,7 @@ namespace Jackett.Controllers
}
catch (Exception ex)
{
logger.Error(ex, "Exception in delete_indexer");
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
@ -276,14 +287,9 @@ namespace Jackett.Controllers
jsonReply["config"] = cfg;
jsonReply["app_version"] = config.GetVersion();
jsonReply["result"] = "success";
}
catch (CustomException ex)
{
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
catch (Exception ex)
}catch (Exception ex)
{
logger.Error(ex, "Exception in get_jackett_config");
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
@ -357,6 +363,7 @@ namespace Jackett.Controllers
}
catch (Exception ex)
{
logger.Error(ex, "Exception in set_port");
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using System.Net.Http.Headers;
using Jackett.Utils;
using System.Net;
using System.Threading;
namespace Jackett
{
@ -18,15 +19,10 @@ namespace Jackett
public class CurlRequest
{
public string Url { get; private set; }
public string Cookies { get; private set; }
public string Referer { get; private set; }
public HttpMethod Method { get; private set; }
public Dictionary<string, string> PostData { get; set; }
public CurlRequest(HttpMethod method, string url, string cookies = null, string referer = null)
@ -40,49 +36,17 @@ namespace Jackett
public class CurlResponse
{
public Dictionary<string, string> Headers { get; private set; }
public List<string[]> HeaderList { get; private set; }
public byte[] Content { get; private set; }
public HttpStatusCode Status { get; private set;}
public Dictionary<string, string> Cookies { get; private set; }
public List<string> CookiesFlat { get { return Cookies.Select(c => c.Key + "=" + c.Value).ToList(); } }
public string CookieHeader { get { return string.Join("; ", CookiesFlat); } }
public string Cookies { set; get; }
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;
Content = content;
Status = s;
foreach (var h in headers)
{
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]);
}
}
Cookies = cookies;
}
}
@ -101,36 +65,14 @@ namespace Jackett
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)
{
return await Task.Run(() => PerformCurl(curlRequest));
}
public delegate void ErrorMessage(string s);
public static ErrorMessage OnErrorMessage;
public static CurlResponse PerformCurl(CurlRequest curlRequest)
{
lock (instance)
@ -145,11 +87,13 @@ namespace Jackett
easy.UserAgent = BrowserUtil.ChromeUserAgent;
easy.FollowLocation = false;
easy.ConnectTimeout = 20;
easy.WriteFunction = (byte[] buf, int size, int nmemb, object data) =>
{
contentBuffers.Add(buf);
return size * nmemb;
};
easy.HeaderFunction = (byte[] buf, int size, int nmemb, object extraData) =>
{
headerBuffers.Add(buf);
@ -170,7 +114,25 @@ namespace Jackett
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();
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());
@ -179,10 +141,14 @@ namespace Jackett
var headers = new List<string[]>();
var headerCount = 0;
HttpStatusCode status = HttpStatusCode.InternalServerError;
var cookieBuilder = new StringBuilder();
foreach (var headerPart in headerParts)
{
if (headerCount == 0)
{
var split = headerPart.Split(' ');
if (split.Length < 2)
throw new Exception("HTTP Header missing");
var responseCode = int.Parse(headerPart.Split(' ')[1]);
status = (HttpStatusCode)responseCode;
}
@ -191,7 +157,17 @@ namespace Jackett
var keyVal = headerPart.Split(new char[] { ':' }, 2);
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 curlResponse = new CurlResponse(headers, contentBytes, status);
if (!string.IsNullOrEmpty(curlRequest.Cookies))
curlResponse.AddCookiesFromHeaderValue(curlRequest.Cookies);
curlResponse.AddCookiesFromHeaders(headers);
var curlResponse = new CurlResponse(headers, contentBytes, status, cookieBuilder.ToString().TrimEnd());
return curlResponse;
}
}

View File

@ -119,7 +119,7 @@ namespace Jackett
logFile.FileName = Path.Combine(ConfigurationService.GetAppDataFolderStatic(), "log.txt");
logFile.ArchiveFileName = "log.{#####}.txt";
logFile.ArchiveAboveSize = 500000;
logFile.MaxArchiveFiles = 1;
logFile.MaxArchiveFiles = 5;
logFile.KeepFileOpen = false;
logFile.ArchiveNumbering = ArchiveNumberingMode.DateAndSequence;
var logFileRule = new LoggingRule("*", logLevel, logFile);

View File

@ -18,11 +18,4 @@ namespace Jackett
}
}
public class CustomException : Exception
{
public CustomException(string message)
: base(message)
{ }
}
}

View File

@ -47,8 +47,10 @@ namespace Jackett.Indexers
{ "submit", "come on in" }
};
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, SiteLink, LoginUrl);
ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("nav_profile"), () =>
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
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;
var messageEl = dom["#login_box_desc"];

View File

@ -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!
await ProcessPage(releases, searchUrl);
await ProcessPage(releases, searchUrl + "&page=1");
return releases;
}
private async Task ProcessPage(List<ReleaseInfo> releases, string searchUrl)
{
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
var results = response.Content;
try
{

View File

@ -40,6 +40,12 @@ namespace Jackett
set;
}
public static bool? DoSSLFix
{
get;
set;
}
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.

View File

@ -29,7 +29,7 @@ namespace Jackett.Utils.Clients
{
logger.Debug(string.Format("WindowsWebClient:GetBytes(Url:{0})", request.Url));
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;
}
@ -37,7 +37,7 @@ namespace Jackett.Utils.Clients
{
logger.Debug(string.Format("WindowsWebClient:GetString(Url:{0})", request.Url));
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);
}

View File

@ -27,7 +27,7 @@ namespace Jackett.Utils.Clients
{
logger.Debug(string.Format("UnixLibCurlWebClient:GetBytes(Url:{0})", request.Url));
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;
}
@ -35,14 +35,35 @@ namespace Jackett.Utils.Clients
{
logger.Debug(string.Format("UnixLibCurlWebClient:GetString(Url:{0})", request.Url));
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);
}
public void Init()
{
Engine.Logger.Info("LibCurl init " + Curl.GlobalInit(CurlInitFlag.All).ToString());
Engine.Logger.Info("LibCurl version " + Curl.Version);
try {
Engine.Logger.Info("LibCurl init " + Curl.GlobalInit(CurlInitFlag.All).ToString());
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)
@ -54,23 +75,30 @@ namespace Jackett.Utils.Clients
}
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);
}
var result = new WebClientByteResult()
{
Content = response.Content,
Cookies = response.CookieHeader,
Cookies = response.Cookies,
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;
}
}
}

View File

@ -1,4 +1,5 @@
using AutoMapper;
using CurlSharp;
using Jackett.Models;
using Jackett.Services;
using NLog;
@ -32,7 +33,7 @@ namespace Jackett.Utils.Clients
{
logger.Debug(string.Format("UnixSafeCurlWebClient:GetBytes(Url:{0})", request.Url));
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;
}
@ -40,7 +41,7 @@ namespace Jackett.Utils.Clients
{
logger.Debug(string.Format("UnixSafeCurlWebClient:GetString(Url:{0})", request.Url));
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);
}
@ -69,6 +70,13 @@ namespace Jackett.Utils.Clients
var tempFile = Path.GetTempFileName();
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;
await Task.Run(() =>
{