Refactor controllers for ASP.NET Core (Authentication disabled for now)

This commit is contained in:
flightlevel
2018-05-01 22:55:09 +10:00
parent a752683965
commit f162902b36
7 changed files with 261 additions and 283 deletions

View File

@@ -1,39 +1,37 @@
using Newtonsoft.Json.Linq; using Jackett.Common.Models.Config;
using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using NLog; using NLog;
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using Jackett.Common.Models.Config;
using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils;
namespace Jackett.Controllers namespace Jackett.Server.Controllers
{ {
[AllowAnonymous] //[AllowAnonymous]
[JackettAPINoCache] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public class BlackholeController : ApiController [Route("bh/{indexerID}")]
public class BlackholeController : Controller
{ {
private Logger logger; private Logger logger;
private IIndexerManagerService indexerService; private IIndexerManagerService indexerService;
private readonly ServerConfig serverConfig; private readonly ServerConfig serverConfig;
IProtectionService protectionService; private IProtectionService protectionService;
public BlackholeController(IIndexerManagerService i, Logger l, ServerConfig config, IProtectionService ps) public BlackholeController(IIndexerManagerService i, Logger l, ServerConfig config, IProtectionService ps)
{ {
logger = l; logger = l;
indexerService = i; indexerService = i;
serverConfig = config; serverConfig = config;
protectionService = ps; protectionService = ps;
} }
[HttpGet] [HttpGet]
public async Task<IHttpActionResult> Blackhole(string indexerID, string path, string jackett_apikey, string file) public async Task<IActionResult> Blackhole(string indexerID, string path, string jackett_apikey, string file)
{ {
var jsonReply = new JObject(); var jsonReply = new JObject();
try try
{ {
@@ -47,7 +45,7 @@ namespace Jackett.Controllers
if (serverConfig.APIKey != jackett_apikey) if (serverConfig.APIKey != jackett_apikey)
throw new Exception("Incorrect API key"); throw new Exception("Incorrect API key");
path = Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(path)); path = WebUtility.UrlDecode(path);
path = protectionService.UnProtect(path); path = protectionService.UnProtect(path);
var remoteFile = new Uri(path, UriKind.RelativeOrAbsolute); var remoteFile = new Uri(path, UriKind.RelativeOrAbsolute);
var fileExtension = ".torrent"; var fileExtension = ".torrent";
@@ -81,9 +79,9 @@ namespace Jackett.Controllers
if (string.IsNullOrWhiteSpace(file)) if (string.IsNullOrWhiteSpace(file))
fileName += fileExtension; fileName += fileExtension;
else else
fileName += "-"+StringUtil.MakeValidFileName(file + fileExtension, '_', false); // call MakeValidFileName() again to avoid any possibility of path traversal attacks fileName += "-" + StringUtil.MakeValidFileName(file + fileExtension, '_', false); // call MakeValidFileName() again to avoid any possibility of path traversal attacks
File.WriteAllBytes(Path.Combine(serverConfig.BlackholeDir, fileName), downloadBytes); System.IO.File.WriteAllBytes(Path.Combine(serverConfig.BlackholeDir, fileName), downloadBytes);
jsonReply["result"] = "success"; jsonReply["result"] = "success";
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -1,26 +1,24 @@
using System; using BencodeNET.Parsing;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using BencodeNET.Parsing;
using Jackett.Common.Models.Config; using Jackett.Common.Models.Config;
using Jackett.Common.Services.Interfaces; using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils; using Jackett.Common.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using NLog; using NLog;
using System;
using System.Text;
using System.Threading.Tasks;
namespace Jackett.Controllers namespace Jackett.Server.Controllers
{ {
[AllowAnonymous] //[AllowAnonymous]
[JackettAPINoCache] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public class DownloadController : ApiController [Route("dl/{indexerID}")]
public class DownloadController : Controller
{ {
private ServerConfig config; private ServerConfig config;
private Logger logger; private Logger logger;
private IIndexerManagerService indexerService; private IIndexerManagerService indexerService;
private IProtectionService protectionService; private IProtectionService protectionService;
public DownloadController(IIndexerManagerService i, Logger l, IProtectionService ps, ServerConfig serverConfig) public DownloadController(IIndexerManagerService i, Logger l, IProtectionService ps, ServerConfig serverConfig)
@@ -32,7 +30,7 @@ namespace Jackett.Controllers
} }
[HttpGet] [HttpGet]
public async Task<HttpResponseMessage> Download(string indexerID, string path, string jackett_apikey, string file) public async Task<IActionResult> Download(string indexerID, string path, string jackett_apikey, string file)
{ {
try try
{ {
@@ -41,14 +39,14 @@ namespace Jackett.Controllers
if (!indexer.IsConfigured) if (!indexer.IsConfigured)
{ {
logger.Warn(string.Format("Rejected a request to {0} which is unconfigured.", indexer.DisplayName)); logger.Warn(string.Format("Rejected a request to {0} which is unconfigured.", indexer.DisplayName));
return Request.CreateResponse(HttpStatusCode.Forbidden, "This indexer is not configured."); return Forbid("This indexer is not configured.");
} }
path = Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(path)); path = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(path));
path = protectionService.UnProtect(path); path = protectionService.UnProtect(path);
if (config.APIKey != jackett_apikey) if (config.APIKey != jackett_apikey)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return Unauthorized();
var target = new Uri(path, UriKind.RelativeOrAbsolute); var target = new Uri(path, UriKind.RelativeOrAbsolute);
var downloadBytes = await indexer.Download(target); var downloadBytes = await indexer.Download(target);
@@ -65,9 +63,7 @@ namespace Jackett.Controllers
) )
{ {
var magneturi = Encoding.UTF8.GetString(downloadBytes); var magneturi = Encoding.UTF8.GetString(downloadBytes);
var response = Request.CreateResponse(HttpStatusCode.Moved); return Redirect(new Uri(magneturi).ToString());
response.Headers.Location = new Uri(magneturi);
return response;
} }
// This will fix torrents where the keys are not sorted, and thereby not supported by Sonarr. // This will fix torrents where the keys are not sorted, and thereby not supported by Sonarr.
@@ -75,19 +71,14 @@ namespace Jackett.Controllers
var torrentDictionary = parser.Parse(downloadBytes); var torrentDictionary = parser.Parse(downloadBytes);
byte[] sortedDownloadBytes = torrentDictionary.EncodeAsBytes(); byte[] sortedDownloadBytes = torrentDictionary.EncodeAsBytes();
var result = new HttpResponseMessage(HttpStatusCode.OK); string fileName = StringUtil.MakeValidFileName(file, '_', false) + ".torrent"; // call MakeValidFileName again to avoid any kind of injection attack
result.Content = new ByteArrayContent(sortedDownloadBytes);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-bittorrent"); return File(sortedDownloadBytes, "application/x-bittorrent", fileName);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = StringUtil.MakeValidFileName(file, '_', false) + ".torrent" // call MakeValidFileName again to avoid any kind of injection attack
};
return result;
} }
catch (Exception e) catch (Exception e)
{ {
logger.Error(e, "Error downloading " + indexerID + " " + path); logger.Error(e, "Error downloading " + indexerID + " " + path);
return new HttpResponseMessage(HttpStatusCode.NotFound); return NotFound();
} }
} }
} }

View File

@@ -1,21 +1,18 @@
using System; using Jackett.Common.Indexers;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Jackett.Common;
using Jackett.Common.Indexers;
using Jackett.Common.Models; using Jackett.Common.Models;
using Jackett.Common.Services.Interfaces; using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils; using Jackett.Common.Utils;
using Jackett.Utils; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog; using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Jackett.Controllers namespace Jackett.Server.Controllers
{ {
public interface IIndexerController public interface IIndexerController
{ {
@@ -23,19 +20,17 @@ namespace Jackett.Controllers
IIndexer CurrentIndexer { get; set; } IIndexer CurrentIndexer { get; set; }
} }
public class RequiresIndexerAttribute : ActionFilterAttribute public class RequiresIndexer : IActionFilter
{ {
public override void OnActionExecuting(HttpActionContext actionContext) public void OnActionExecuting(ActionExecutingContext context)
{ {
base.OnActionExecuting(actionContext); var controller = context.Controller;
var controller = actionContext.ControllerContext.Controller;
if (!(controller is IIndexerController)) if (!(controller is IIndexerController))
return; return;
var indexerController = controller as IIndexerController; var indexerController = controller as IIndexerController;
var parameters = actionContext.RequestContext.RouteData.Values; var parameters = context.RouteData.Values;
if (!parameters.ContainsKey("indexerId")) if (!parameters.ContainsKey("indexerId"))
{ {
@@ -51,15 +46,23 @@ namespace Jackett.Controllers
var indexer = indexerService.GetIndexer(indexerId); var indexer = indexerService.GetIndexer(indexerId);
indexerController.CurrentIndexer = indexer; indexerController.CurrentIndexer = indexer;
} }
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
}
} }
[RoutePrefix("api/v2.0/indexers")] [Route("api/v2.0/indexers")]
[JackettAuthorized] //[JackettAuthorized]
[JackettAPINoCache] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public class IndexerApiController : ApiController, IIndexerController public class IndexerApiController : Controller, IIndexerController
{ {
public IIndexerManagerService IndexerService { get; private set; } public IIndexerManagerService IndexerService { get; private set; }
public IIndexer CurrentIndexer { get; set; } public IIndexer CurrentIndexer { get; set; }
private Logger logger;
private IServerService serverService;
private ICacheService cacheService;
public IndexerApiController(IIndexerManagerService indexerManagerService, IServerService ss, ICacheService c, Logger logger) public IndexerApiController(IIndexerManagerService indexerManagerService, IServerService ss, ICacheService c, Logger logger)
{ {
@@ -70,16 +73,17 @@ namespace Jackett.Controllers
} }
[HttpGet] [HttpGet]
[RequiresIndexer] [TypeFilter(typeof(RequiresIndexer))]
public async Task<IHttpActionResult> Config() [Route("{indexerId?}/Config")]
public async Task<IActionResult> Config()
{ {
var config = await CurrentIndexer.GetConfigurationForSetup(); var config = await CurrentIndexer.GetConfigurationForSetup();
return Ok(config.ToJson(null)); return Ok(config.ToJson(null));
} }
[HttpPost] [HttpPost]
[ActionName("Config")] [Route("{indexerId?}/Config")]
[RequiresIndexer] [TypeFilter(typeof(RequiresIndexer))]
public async Task UpdateConfig([FromBody]Common.Models.DTO.ConfigItem[] config) public async Task UpdateConfig([FromBody]Common.Models.DTO.ConfigItem[] config)
{ {
try try
@@ -111,14 +115,16 @@ namespace Jackett.Controllers
} }
[HttpPost] [HttpPost]
[RequiresIndexer] [Route("{indexerid}/[action]")]
public async Task Test() [TypeFilter(typeof(RequiresIndexer))]
public async Task<IActionResult> Test()
{ {
JToken jsonReply = new JObject(); JToken jsonReply = new JObject();
try try
{ {
await IndexerService.TestIndexer(CurrentIndexer.ID); await IndexerService.TestIndexer(CurrentIndexer.ID);
CurrentIndexer.LastError = null; CurrentIndexer.LastError = null;
return NoContent();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -134,8 +140,8 @@ namespace Jackett.Controllers
} }
[HttpDelete] [HttpDelete]
[RequiresIndexer] [TypeFilter(typeof(RequiresIndexer))]
[Route("{indexerId}")] [Route("{indexerid}")]
public void Delete() public void Delete()
{ {
IndexerService.DeleteIndexer(CurrentIndexer.ID); IndexerService.DeleteIndexer(CurrentIndexer.ID);
@@ -160,14 +166,10 @@ namespace Jackett.Controllers
var link = result.Link; var link = result.Link;
var file = StringUtil.MakeValidFileName(result.Title, '_', false); var file = StringUtil.MakeValidFileName(result.Title, '_', false);
result.Link = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "dl", file); result.Link = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "dl", file);
if (result.Link != null && result.Link.Scheme != "magnet" && !string.IsNullOrWhiteSpace(Engine.ServerConfig.BlackholeDir)) if (result.Link != null && result.Link.Scheme != "magnet" && !string.IsNullOrWhiteSpace(serverService.GetBlackholeDirectory()))
result.BlackholeLink = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "bh", file); result.BlackholeLink = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "bh", file);
} }
} }
private Logger logger;
private IServerService serverService;
private ICacheService cacheService;
} }
} }

View File

@@ -1,59 +1,73 @@
using System; using Jackett.Common;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Xml.Linq;
using Jackett.Common;
using Jackett.Common.Indexers; using Jackett.Common.Indexers;
using Jackett.Common.Indexers.Meta; using Jackett.Common.Indexers.Meta;
using Jackett.Common.Models; using Jackett.Common.Models;
using Jackett.Common.Models.DTO; using Jackett.Common.Models.DTO;
using Jackett.Common.Services.Interfaces; using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils; using Jackett.Common.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using NLog; using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Jackett.Controllers namespace Jackett.Server.Controllers
{ {
public class RequiresApiKeyAttribute : AuthorizationFilterAttribute public class RequiresApiKey : IActionFilter
{ {
public override void OnAuthorization(HttpActionContext actionContext) public IServerService serverService;
public RequiresApiKey(IServerService ss)
{ {
var validApiKey = Engine.ServerConfig.APIKey; serverService = ss;
var queryParams = actionContext.Request.GetQueryNameValuePairs(); }
public void OnActionExecuting(ActionExecutingContext context)
{
var validApiKey = serverService.GetApiKey();
var queryParams = context.HttpContext.Request.Query;
var queryApiKey = queryParams.Where(x => x.Key == "apikey" || x.Key == "passkey").Select(x => x.Value).FirstOrDefault(); var queryApiKey = queryParams.Where(x => x.Key == "apikey" || x.Key == "passkey").Select(x => x.Value).FirstOrDefault();
#if DEBUG #if DEBUG
if (Debugger.IsAttached) if (Debugger.IsAttached)
{
return; return;
}
#endif #endif
if (queryApiKey != validApiKey) if (queryApiKey != validApiKey)
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); {
context.Result = new UnauthorizedResult();
return;
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
} }
} }
public class RequiresConfiguredIndexerAttribute : ActionFilterAttribute public class RequiresConfiguredIndexer : IActionFilter
{ {
public override void OnActionExecuting(HttpActionContext actionContext) public void OnActionExecuting(ActionExecutingContext context)
{ {
var controller = actionContext.ControllerContext.Controller; var controller = context.Controller;
if (!(controller is IIndexerController)) if (!(controller is IIndexerController))
return; return;
var indexerController = controller as IIndexerController; var indexerController = controller as IIndexerController;
var parameters = actionContext.RequestContext.RouteData.Values; var parameters = context.RouteData.Values;
if (!parameters.ContainsKey("indexerId")) if (!parameters.ContainsKey("indexerId"))
{ {
indexerController.CurrentIndexer = null; indexerController.CurrentIndexer = null;
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid parameter"); context.Result = new UnauthorizedResult();
return; return;
} }
@@ -61,7 +75,7 @@ namespace Jackett.Controllers
if (indexerId.IsNullOrEmptyOrWhitespace()) if (indexerId.IsNullOrEmptyOrWhitespace())
{ {
indexerController.CurrentIndexer = null; indexerController.CurrentIndexer = null;
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid parameter"); context.Result = new UnauthorizedResult();
return; return;
} }
@@ -71,63 +85,68 @@ namespace Jackett.Controllers
if (indexer == null) if (indexer == null)
{ {
indexerController.CurrentIndexer = null; indexerController.CurrentIndexer = null;
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid parameter"); context.Result = new UnauthorizedResult();
return; return;
} }
if (!indexer.IsConfigured) if (!indexer.IsConfigured)
{ {
indexerController.CurrentIndexer = null; indexerController.CurrentIndexer = null;
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Indexer is not configured"); context.Result = new UnauthorizedResult();
return; return;
} }
indexerController.CurrentIndexer = indexer; indexerController.CurrentIndexer = indexer;
} }
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
}
} }
public class RequiresValidQueryAttribute : RequiresConfiguredIndexerAttribute public class RequiresValidQuery : IActionFilter
{ {
public override void OnActionExecuting(HttpActionContext actionContext) public void OnActionExecuting(ActionExecutingContext context)
{ {
base.OnActionExecuting(actionContext); //TODO: Not sure what this is meant to do
if (actionContext.Response != null) //if (context.HttpContext.Response != null)
return; // return;
var controller = actionContext.ControllerContext.Controller; var controller = context.Controller;
if (!(controller is IResultController)) if (!(controller is IResultController))
{
return; return;
}
var resultController = controller as IResultController; var resultController = controller as IResultController;
var query = actionContext.ActionArguments.First().Value; var query = context.ActionArguments.First().Value;
var queryType = query.GetType(); var queryType = query.GetType();
var converter = queryType.GetMethod("ToTorznabQuery", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); var converter = queryType.GetMethod("ToTorznabQuery", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
if (converter == null) if (converter == null)
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, ""); {
context.Result = new BadRequestResult();
}
var converted = converter.Invoke(null, new object[] { query }); var converted = converter.Invoke(null, new object[] { query });
var torznabQuery = converted as TorznabQuery; var torznabQuery = converted as TorznabQuery;
resultController.CurrentQuery = torznabQuery; resultController.CurrentQuery = torznabQuery;
if (queryType == typeof(ApiSearch)) // Skip CanHandleQuery() check for manual search (CurrentIndexer isn't used during manul search) if (queryType == typeof(ApiSearch)) // Skip CanHandleQuery() check for manual search (CurrentIndexer isn't used during manul search)
{
return; return;
}
if (!resultController.CurrentIndexer.CanHandleQuery(resultController.CurrentQuery)) if (!resultController.CurrentIndexer.CanHandleQuery(resultController.CurrentQuery))
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, $"{resultController.CurrentIndexer.ID} does not support the requested query. Please check the capabilities (t=caps) and make sure the search mode and categories are supported."); {
context.Result = new BadRequestObjectResult($"{resultController.CurrentIndexer.ID} does not support the requested query. Please check the capabilities (t=caps) and make sure the search mode and categories are supported.");
}
} }
}
public class JsonResponseAttribute : ActionFilterAttribute public void OnActionExecuted(ActionExecutedContext context)
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{ {
base.OnActionExecuted(actionExecutedContext); // do something after the action executes
if (actionExecutedContext.Exception != null)
throw new Exception("Error while executing request", actionExecutedContext.Exception);
var content = actionExecutedContext.Response.Content as ObjectContent;
actionExecutedContext.Response.Content = new JsonContent(content.Value);
} }
} }
@@ -136,12 +155,13 @@ namespace Jackett.Controllers
TorznabQuery CurrentQuery { get; set; } TorznabQuery CurrentQuery { get; set; }
} }
[AllowAnonymous] //[AllowAnonymous]
[JackettAPINoCache] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
[RoutePrefix("api/v2.0/indexers")] [Route("api/v2.0/indexers/{indexerId}/results")]
[RequiresApiKey] [TypeFilter(typeof(RequiresApiKey))]
[RequiresValidQuery] [TypeFilter(typeof(RequiresConfiguredIndexer))]
public class ResultsController : ApiController, IResultController [TypeFilter(typeof(RequiresValidQuery))]
public class ResultsController : Controller, IResultController
{ {
public IIndexerManagerService IndexerService { get; private set; } public IIndexerManagerService IndexerService { get; private set; }
public IIndexer CurrentIndexer { get; set; } public IIndexer CurrentIndexer { get; set; }
@@ -155,13 +175,39 @@ namespace Jackett.Controllers
this.logger = logger; this.logger = logger;
} }
[Route("")]
[HttpGet] [HttpGet]
public async Task<ManualSearchResult> Results([FromUri]ApiSearch request) public async Task<IActionResult> Results([FromQuery] ApiSearch requestt)
{ {
//TODO: Better way to parse querystring
ApiSearch request = new ApiSearch();
foreach (var t in Request.Query)
{
if (t.Key == "Tracker[]")
{
request.Tracker = t.Value.ToString().Split(",");
}
if (t.Key == "Category[]")
{
request.Category = t.Value.ToString().Split(",").Select(Int32.Parse).ToArray();
}
if (t.Key == "query")
{
request.Query = t.Value.ToString();
}
}
var manualResult = new ManualSearchResult(); var manualResult = new ManualSearchResult();
var trackers = IndexerService.GetAllIndexers().Where(t => t.IsConfigured); var trackers = IndexerService.GetAllIndexers().ToList().Where(t => t.IsConfigured);
if (request.Tracker != null) if (request.Tracker != null)
{
trackers = trackers.Where(t => request.Tracker.Contains(t.ID)); trackers = trackers.Where(t => request.Tracker.Contains(t.ID));
}
trackers = trackers.Where(t => t.CanHandleQuery(CurrentQuery)); trackers = trackers.Where(t => t.CanHandleQuery(CurrentQuery));
var tasks = trackers.ToList().Select(t => t.ResultsForQuery(CurrentQuery)).ToList(); var tasks = trackers.ToList().Select(t => t.ResultsForQuery(CurrentQuery)).ToList();
@@ -235,18 +281,16 @@ namespace Jackett.Controllers
ConfigureCacheResults(manualResult.Results); ConfigureCacheResults(manualResult.Results);
logger.Info(string.Format("Manual search for \"{0}\" on {1} with {2} results.", CurrentQuery.SanitizedSearchTerm, string.Join(", ", manualResult.Indexers.Select(i => i.ID)), manualResult.Results.Count())); logger.Info(string.Format("Manual search for \"{0}\" on {1} with {2} results.", CurrentQuery.SanitizedSearchTerm, string.Join(", ", manualResult.Indexers.Select(i => i.ID)), manualResult.Results.Count()));
return manualResult; return Json(manualResult);
} }
[Route("[action]/{ignored?}")]
[HttpGet] [HttpGet]
public async Task<IHttpActionResult> Torznab([FromUri]Common.Models.DTO.TorznabRequest request) public async Task<IActionResult> Torznab([FromQuery]TorznabRequest request)
{ {
if (string.Equals(CurrentQuery.QueryType, "caps", StringComparison.InvariantCultureIgnoreCase)) if (string.Equals(CurrentQuery.QueryType, "caps", StringComparison.InvariantCultureIgnoreCase))
{ {
return ResponseMessage(new HttpResponseMessage() return Content(CurrentIndexer.TorznabCaps.ToXml(), "application/rss+xml", Encoding.UTF8);
{
Content = new StringContent(CurrentIndexer.TorznabCaps.ToXml(), Encoding.UTF8, "application/xml")
});
} }
// indexers - returns a list of all included indexers (meta indexers only) // indexers - returns a list of all included indexers (meta indexers only)
@@ -254,7 +298,7 @@ namespace Jackett.Controllers
{ {
if (!(CurrentIndexer is BaseMetaIndexer)) // shouldn't be needed because CanHandleQuery should return false if (!(CurrentIndexer is BaseMetaIndexer)) // shouldn't be needed because CanHandleQuery should return false
{ {
logger.Warn($"A search request with t=indexers from {Request.GetOwinContext().Request.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} isn't a meta indexer."); logger.Warn($"A search request with t=indexers from {Request.HttpContext.Connection.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} isn't a meta indexer.");
return GetErrorXML(203, "Function Not Available: this isn't a meta indexer"); return GetErrorXML(203, "Function Not Available: this isn't a meta indexer");
} }
var CurrentBaseMetaIndexer = (BaseMetaIndexer)CurrentIndexer; var CurrentBaseMetaIndexer = (BaseMetaIndexer)CurrentIndexer;
@@ -281,30 +325,27 @@ namespace Jackett.Controllers
) )
); );
return ResponseMessage(new HttpResponseMessage() return Content(xdoc.Declaration.ToString() + Environment.NewLine + xdoc.ToString(), "application/xml", Encoding.UTF8);
{
Content = new StringContent(xdoc.Declaration.ToString() + Environment.NewLine + xdoc.ToString(), Encoding.UTF8, "application/xml")
});
} }
if (CurrentQuery.ImdbID != null) if (CurrentQuery.ImdbID != null)
{ {
if (!string.IsNullOrEmpty(CurrentQuery.SearchTerm)) if (!string.IsNullOrEmpty(CurrentQuery.SearchTerm))
{ {
logger.Warn($"A search request from {Request.GetOwinContext().Request.RemoteIpAddress} was made containing q and imdbid."); logger.Warn($"A search request from {Request.HttpContext.Connection.RemoteIpAddress} was made containing q and imdbid.");
return GetErrorXML(201, "Incorrect parameter: please specify either imdbid or q"); return GetErrorXML(201, "Incorrect parameter: please specify either imdbid or q");
} }
CurrentQuery.ImdbID = ParseUtil.GetFullImdbID(CurrentQuery.ImdbID); // normalize ImdbID CurrentQuery.ImdbID = ParseUtil.GetFullImdbID(CurrentQuery.ImdbID); // normalize ImdbID
if (CurrentQuery.ImdbID == null) if (CurrentQuery.ImdbID == null)
{ {
logger.Warn($"A search request from {Request.GetOwinContext().Request.RemoteIpAddress} was made with an invalid imdbid."); logger.Warn($"A search request from {Request.HttpContext.Connection.RemoteIpAddress} was made with an invalid imdbid.");
return GetErrorXML(201, "Incorrect parameter: invalid imdbid format"); return GetErrorXML(201, "Incorrect parameter: invalid imdbid format");
} }
if (!CurrentIndexer.TorznabCaps.SupportsImdbSearch) if (!CurrentIndexer.TorznabCaps.SupportsImdbSearch)
{ {
logger.Warn($"A search request with imdbid from {Request.GetOwinContext().Request.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} doesn't support it."); logger.Warn($"A search request with imdbid from {Request.HttpContext.Connection.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} doesn't support it.");
return GetErrorXML(203, "Function Not Available: imdbid is not supported by this indexer"); return GetErrorXML(203, "Function Not Available: imdbid is not supported by this indexer");
} }
} }
@@ -361,13 +402,12 @@ namespace Jackett.Controllers
var xml = resultPage.ToXml(new Uri(serverUrl)); var xml = resultPage.ToXml(new Uri(serverUrl));
// Force the return as XML // Force the return as XML
return ResponseMessage(new HttpResponseMessage()
{ return Content(xml, "application/rss+xml", Encoding.UTF8);
Content = new StringContent(xml, Encoding.UTF8, "application/rss+xml")
});
} }
public IHttpActionResult GetErrorXML(int code, string description) [Route("[action]/{ignored?}")]
public IActionResult GetErrorXML(int code, string description)
{ {
var xdoc = new XDocument( var xdoc = new XDocument(
new XDeclaration("1.0", "UTF-8", null), new XDeclaration("1.0", "UTF-8", null),
@@ -378,16 +418,12 @@ namespace Jackett.Controllers
); );
var xml = xdoc.Declaration.ToString() + Environment.NewLine + xdoc.ToString(); var xml = xdoc.Declaration.ToString() + Environment.NewLine + xdoc.ToString();
return Content(xml, "application/xml", Encoding.UTF8);
return ResponseMessage(new HttpResponseMessage()
{
Content = new StringContent(xml, Encoding.UTF8, "application/xml")
});
} }
[Route("[action]/{ignored?}")]
[HttpGet] [HttpGet]
[JsonResponse] public async Task<TorrentPotatoResponse> Potato([FromQuery]TorrentPotatoRequest request)
public async Task<TorrentPotatoResponse> Potato([FromUri]TorrentPotatoRequest request)
{ {
var result = await CurrentIndexer.ResultsForQuery(CurrentQuery); var result = await CurrentIndexer.ResultsForQuery(CurrentQuery);
@@ -431,6 +467,7 @@ namespace Jackett.Controllers
return potatoResponse; return potatoResponse;
} }
[Route("[action]/{ignored?}")]
private void ConfigureCacheResults(IEnumerable<TrackerCacheResult> results) private void ConfigureCacheResults(IEnumerable<TrackerCacheResult> results)
{ {
var serverUrl = serverService.GetServerUrl(Request); var serverUrl = serverService.GetServerUrl(Request);
@@ -439,9 +476,8 @@ namespace Jackett.Controllers
var link = result.Link; var link = result.Link;
var file = StringUtil.MakeValidFileName(result.Title, '_', false); var file = StringUtil.MakeValidFileName(result.Title, '_', false);
result.Link = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "dl", file); result.Link = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "dl", file);
if (result.Link != null && result.Link.Scheme != "magnet" && !string.IsNullOrWhiteSpace(Engine.ServerConfig.BlackholeDir)) if (result.Link != null && result.Link.Scheme != "magnet" && !string.IsNullOrWhiteSpace(serverService.GetBlackholeDirectory()))
result.BlackholeLink = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "bh", file); result.BlackholeLink = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "bh", file);
} }
} }

View File

@@ -1,23 +1,20 @@
using System; using Jackett.Common.Models;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Web.Http;
using Jackett.Common;
using Jackett.Common.Models;
using Jackett.Common.Models.Config; using Jackett.Common.Models.Config;
using Jackett.Common.Services.Interfaces; using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils; using Jackett.Common.Utils;
using Jackett.Utils; using Microsoft.AspNetCore.Mvc;
using NLog; using NLog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Jackett.Controllers namespace Jackett.Server.Controllers
{ {
[RoutePrefix("api/v2.0/server")] [Route("api/v2.0/server/[action]")]
[JackettAuthorized] //[JackettAuthorized]
[JackettAPINoCache] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public class ServerConfigurationController : ApiController public class ServerConfigurationController : Controller
{ {
private readonly IConfigurationService configService; private readonly IConfigurationService configService;
private ServerConfig serverConfig; private ServerConfig serverConfig;
@@ -29,7 +26,7 @@ namespace Jackett.Controllers
private ILogCacheService logCache; private ILogCacheService logCache;
private Logger logger; private Logger logger;
public ServerConfigurationController(IConfigurationService c, IServerService s, IProcessService p, IIndexerManagerService i, ISecuityService ss, IUpdateService u, ILogCacheService lc, Logger l, ServerConfig sc) public ServerConfigurationController(IConfigurationService c, IServerService s, IProcessService p, IIndexerManagerService i, ISecuityService ss, IUpdateService u, ILogCacheService lc, Logger l, ServerConfig sc)
{ {
configService = c; configService = c;
serverConfig = sc; serverConfig = sc;
@@ -65,14 +62,13 @@ namespace Jackett.Controllers
[HttpGet] [HttpGet]
public Common.Models.DTO.ServerConfig Config() public Common.Models.DTO.ServerConfig Config()
{ {
var dto = new Common.Models.DTO.ServerConfig(serverService.notices, serverConfig, configService.GetVersion()); var dto = new Common.Models.DTO.ServerConfig(serverService.notices, serverConfig, configService.GetVersion());
return dto; return dto;
} }
[ActionName("Config")] [ActionName("Config")]
[HttpPost] [HttpPost]
public void UpdateConfig([FromBody]Common.Models.DTO.ServerConfig config) public IActionResult UpdateConfig([FromBody]Common.Models.DTO.ServerConfig config)
{ {
var originalPort = serverConfig.Port; var originalPort = serverConfig.Port;
var originalAllowExternal = serverConfig.AllowExternal; var originalAllowExternal = serverConfig.AllowExternal;
@@ -95,16 +91,16 @@ namespace Jackett.Controllers
serverConfig.UpdateDisabled = updateDisabled; serverConfig.UpdateDisabled = updateDisabled;
serverConfig.UpdatePrerelease = preRelease; serverConfig.UpdatePrerelease = preRelease;
serverConfig.BasePathOverride = basePathOverride; serverConfig.BasePathOverride = basePathOverride;
serverConfig.RuntimeSettings.BasePath = Engine.Server.BasePath(); serverConfig.RuntimeSettings.BasePath = serverService.BasePath();
configService.SaveConfig(serverConfig); configService.SaveConfig(serverConfig);
Engine.SetLogLevel(logging ? LogLevel.Debug : LogLevel.Info); Initialisation.SetLogLevel(logging ? LogLevel.Debug : LogLevel.Info);
serverConfig.RuntimeSettings.TracingEnabled = logging; serverConfig.RuntimeSettings.TracingEnabled = logging;
if (omdbApiKey != serverConfig.OmdbApiKey) if (omdbApiKey != serverConfig.OmdbApiKey)
{ {
serverConfig.OmdbApiKey = omdbApiKey; serverConfig.OmdbApiKey = omdbApiKey;
configService.SaveConfig(serverConfig); configService.SaveConfig(serverConfig);
// HACK // HACK
indexerService.InitAggregateIndexer(); indexerService.InitAggregateIndexer();
} }
@@ -128,7 +124,6 @@ namespace Jackett.Controllers
if (port != serverConfig.Port || external != serverConfig.AllowExternal) if (port != serverConfig.Port || external != serverConfig.AllowExternal)
{ {
if (ServerUtil.RestrictedPorts.Contains(port)) if (ServerUtil.RestrictedPorts.Contains(port))
throw new Exception("The port you have selected is restricted, try a different one."); throw new Exception("The port you have selected is restricted, try a different one.");
@@ -147,7 +142,8 @@ namespace Jackett.Controllers
{ {
try try
{ {
processService.StartProcessAndLog(System.Windows.Forms.Application.ExecutablePath, "--ReserveUrls", true); //TODO
//processService.StartProcessAndLog(System.Windows.Forms.Application.ExecutablePath, "--ReserveUrls", true);
} }
catch catch
{ {
@@ -163,15 +159,15 @@ namespace Jackett.Controllers
serverService.ReserveUrls(true); serverService.ReserveUrls(true);
} }
} }
//TODO
(new Thread(() => //(new Thread(() =>
{ //{
Thread.Sleep(500); // Thread.Sleep(500);
serverService.Stop(); // serverService.Stop();
Engine.BuildContainer(serverConfig.RuntimeSettings, new WebApi2Module()); // Engine.BuildContainer(serverConfig.RuntimeSettings, new WebApi2Module());
Engine.Server.Initalize(); // Engine.Server.Initalize();
Engine.Server.Start(); // Engine.Server.Start();
})).Start(); //})).Start();
} }
if (saveDir != serverConfig.BlackholeDir) if (saveDir != serverConfig.BlackholeDir)
@@ -187,8 +183,10 @@ namespace Jackett.Controllers
serverConfig.BlackholeDir = saveDir; serverConfig.BlackholeDir = saveDir;
configService.SaveConfig(serverConfig); configService.SaveConfig(serverConfig);
} }
serverConfig.ConfigChanged(); serverConfig.ConfigChanged();
return Json(serverConfig);
} }
[HttpGet] [HttpGet]
@@ -196,7 +194,5 @@ namespace Jackett.Controllers
{ {
return logCache.Logs; return logCache.Logs;
} }
} }
} }

View File

@@ -1,21 +1,20 @@
using System.IO; using Jackett.Common.Models.Config;
using Jackett.Common.Services.Interfaces;
using Microsoft.AspNetCore.Mvc;
using MimeMapping;
using NLog;
using System.IO;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web.Http;
using Jackett.Common.Models.Config;
using Jackett.Common.Services.Interfaces;
using Jackett.Utils;
using MimeMapping;
using NLog;
namespace Jackett.Controllers namespace Jackett.Server.Controllers
{ {
[RoutePrefix("UI")] [Route("UI/[action]")]
[JackettAuthorized] //[JackettAuthorized]
[JackettAPINoCache] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public class WebUIController : ApiController public class WebUIController : Controller
{ {
private IConfigurationService config; private IConfigurationService config;
private ServerConfig serverConfig; private ServerConfig serverConfig;
@@ -42,50 +41,50 @@ namespace Jackett.Controllers
} }
[HttpGet] [HttpGet]
[AllowAnonymous] //[AllowAnonymous]
public IHttpActionResult Logout() public IActionResult Logout()
{ {
var ctx = Request.GetOwinContext(); var ctx = Request.HttpContext;
var authManager = ctx.Authentication; //TODO
authManager.SignOut("ApplicationCookie"); //var authManager = ctx.Authentication;
return Redirect("UI/Dashboard"); //authManager.SignOut("ApplicationCookie");
return Redirect("Dashboard");
} }
[HttpGet] [HttpGet]
[HttpPost] [HttpPost]
[AllowAnonymous] //[AllowAnonymous]
public async Task<HttpResponseMessage> Dashboard() public async Task<HttpResponseMessage> Dashboard()
{ {
if (Request.RequestUri.Query != null && Request.RequestUri.Query.Contains("logout")) if (Request.Path != null && Request.Path.ToString().Contains("logout"))
{ {
var file = GetFile("login.html"); var file = GetFile("login.html");
securityService.Logout(file); securityService.Logout(file);
return file; return file;
} }
//TODO
if (securityService.CheckAuthorised(Request)) //if (securityService.CheckAuthorised(Request))
{ //{
return GetFile("index.html"); return GetFile("index.html");
} //}
else //else
{ //{
var formData = await Request.Content.ReadAsFormDataAsync(); // var formData = await Request.ReadFormAsync();
if (formData != null && securityService.HashPassword(formData["password"]) == serverConfig.AdminPassword) // if (formData != null && securityService.HashPassword(formData["password"]) == serverConfig.AdminPassword)
{ // {
var file = GetFile("index.html"); // var file = GetFile("index.html");
securityService.Login(file); // securityService.Login(file);
return file; // return file;
} // }
else // else
{ // {
return GetFile("login.html"); // return GetFile("login.html");
} // }
} //}
} }
} }
} }

View File

@@ -1,44 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Jackett.Server.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}