using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Web; namespace Jackett { public class WebApi { static string WebContentFolder = "WebContent"; static string[] StaticFiles = Directory.EnumerateFiles(WebContentFolder, "*", SearchOption.AllDirectories).ToArray(); public enum WebApiMethod { GetConfigForm, ConfigureIndexer, GetIndexers, TestIndexer, DeleteIndexer, GetSonarrConfig, ApplySonarrConfig, TestSonarr } static Dictionary WebApiMethods = new Dictionary { { "get_config_form", WebApiMethod.GetConfigForm }, { "configure_indexer", WebApiMethod.ConfigureIndexer }, { "get_indexers", WebApiMethod.GetIndexers }, { "test_indexer", WebApiMethod.TestIndexer }, { "delete_indexer", WebApiMethod.DeleteIndexer }, { "get_sonarr_config", WebApiMethod.GetSonarrConfig }, { "apply_sonarr_config", WebApiMethod.ApplySonarrConfig }, { "test_sonarr", WebApiMethod.TestSonarr } }; IndexerManager indexerManager; SonarrApi sonarrApi; public WebApi(IndexerManager indexerManager, SonarrApi sonarrApi) { this.indexerManager = indexerManager; this.sonarrApi = sonarrApi; } public bool HandleRequest(HttpListenerContext context) { string path = context.Request.Url.AbsolutePath.TrimStart('/'); if (path == "") path = "index.html"; var sysPath = Path.Combine(WebContentFolder, path.Replace("/", Path.DirectorySeparatorChar.ToString())); if (Array.IndexOf(StaticFiles, sysPath) > -1) { ServeStaticFile(context, path); return true; } WebApi.WebApiMethod apiMethod; if (WebApi.WebApiMethods.TryGetValue(path, out apiMethod)) { ProcessWebApiRequest(context, apiMethod); return true; } return false; } async void ServeStaticFile(HttpListenerContext context, string file) { var contentFile = File.ReadAllBytes(Path.Combine(WebContentFolder, file)); context.Response.ContentType = MimeMapping.GetMimeMapping(file); context.Response.StatusCode = (int)HttpStatusCode.OK; try { await context.Response.OutputStream.WriteAsync(contentFile, 0, contentFile.Length); context.Response.OutputStream.Close(); } catch (HttpListenerException) { } } async Task ReadPostDataJson(Stream stream) { string postData = await new StreamReader(stream).ReadToEndAsync(); return JObject.Parse(postData); } delegate Task HandlerTask(HttpListenerContext context); async void ProcessWebApiRequest(HttpListenerContext context, WebApiMethod method) { var query = HttpUtility.ParseQueryString(context.Request.Url.Query); context.Response.ContentType = "text/json"; context.Response.StatusCode = (int)HttpStatusCode.OK; HandlerTask handlerTask; switch (method) { case WebApiMethod.GetConfigForm: handlerTask = HandleConfigForm; break; case WebApiMethod.ConfigureIndexer: handlerTask = HandleConfigureIndexer; break; case WebApiMethod.GetIndexers: handlerTask = HandleGetIndexers; break; case WebApiMethod.TestIndexer: handlerTask = HandleTestIndexer; break; case WebApiMethod.DeleteIndexer: handlerTask = HandleDeleteIndexer; break; case WebApiMethod.GetSonarrConfig: handlerTask = HandleGetSonarrConfig; break; case WebApiMethod.ApplySonarrConfig: handlerTask = HandleApplySonarrConfig; break; case WebApiMethod.TestSonarr: handlerTask = HandleTestSonarr; break; default: handlerTask = HandleInvalidApiMethod; break; } JToken jsonReply = await handlerTask(context); ReplyWithJson(context, jsonReply); } async void ReplyWithJson(HttpListenerContext context, JToken json) { byte[] jsonBytes = Encoding.UTF8.GetBytes(json.ToString()); await context.Response.OutputStream.WriteAsync(jsonBytes, 0, jsonBytes.Length); context.Response.OutputStream.Close(); } async Task HandleTestSonarr(HttpListenerContext context) { JToken jsonReply = new JObject(); try { await sonarrApi.TestConnection(); jsonReply["result"] = "success"; } catch (Exception ex) { jsonReply["result"] = "error"; jsonReply["error"] = ex.Message; } return jsonReply; } async Task HandleApplySonarrConfig(HttpListenerContext context) { JToken jsonReply = new JObject(); try { var postData = await ReadPostDataJson(context.Request.InputStream); await sonarrApi.ApplyConfiguration(postData); jsonReply["result"] = "success"; } catch (Exception ex) { jsonReply["result"] = "error"; jsonReply["error"] = ex.Message; } return jsonReply; } Task HandleGetSonarrConfig(HttpListenerContext context) { JObject jsonReply = new JObject(); try { jsonReply["config"] = sonarrApi.GetConfiguration().ToJson(); jsonReply["result"] = "success"; } catch (Exception ex) { jsonReply["result"] = "error"; jsonReply["error"] = ex.Message; } return Task.FromResult(jsonReply); } Task HandleInvalidApiMethod(HttpListenerContext context) { JToken jsonReply = new JObject(); jsonReply["result"] = "error"; jsonReply["error"] = "Invalid API method"; return Task.FromResult(jsonReply); } async Task HandleConfigForm(HttpListenerContext context) { JToken jsonReply = new JObject(); try { var postData = await ReadPostDataJson(context.Request.InputStream); string indexerString = (string)postData["indexer"]; var indexer = indexerManager.GetIndexer(indexerString); var config = await indexer.GetConfigurationForSetup(); jsonReply["config"] = config.ToJson(); jsonReply["name"] = indexer.DisplayName; jsonReply["result"] = "success"; } catch (Exception ex) { jsonReply["result"] = "error"; jsonReply["error"] = ex.Message; } return jsonReply; } async Task HandleConfigureIndexer(HttpListenerContext context) { JToken jsonReply = new JObject(); try { var postData = await ReadPostDataJson(context.Request.InputStream); string indexerString = (string)postData["indexer"]; var indexer = indexerManager.GetIndexer(indexerString); jsonReply["name"] = indexer.DisplayName; await indexer.ApplyConfiguration(postData["config"]); await indexer.VerifyConnection(); jsonReply["result"] = "success"; } catch (Exception ex) { jsonReply["result"] = "error"; jsonReply["error"] = ex.Message; if (ex is ExceptionWithConfigData) { jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson(); } } return jsonReply; } Task HandleGetIndexers(HttpListenerContext context) { JToken jsonReply = new JObject(); try { jsonReply["result"] = "success"; jsonReply["api_key"] = ApiKey.CurrentKey; JArray items = new JArray(); foreach (var i in indexerManager.Indexers) { var indexer = i.Value; var item = new JObject(); item["id"] = i.Key; item["name"] = indexer.DisplayName; item["description"] = indexer.DisplayDescription; item["configured"] = indexer.IsConfigured; item["site_link"] = indexer.SiteLink; items.Add(item); } jsonReply["items"] = items; } catch (Exception ex) { jsonReply["result"] = "error"; jsonReply["error"] = ex.Message; } return Task.FromResult(jsonReply); } async Task HandleTestIndexer(HttpListenerContext context) { JToken jsonReply = new JObject(); try { var postData = await ReadPostDataJson(context.Request.InputStream); string indexerString = (string)postData["indexer"]; var indexer = indexerManager.GetIndexer(indexerString); jsonReply["name"] = indexer.DisplayName; await indexer.VerifyConnection(); jsonReply["result"] = "success"; } catch (Exception ex) { jsonReply["result"] = "error"; jsonReply["error"] = ex.Message; } return jsonReply; } async Task HandleDeleteIndexer(HttpListenerContext context) { JToken jsonReply = new JObject(); try { var postData = await ReadPostDataJson(context.Request.InputStream); string indexerString = (string)postData["indexer"]; indexerManager.DeleteIndexer(indexerString); } catch (Exception ex) { jsonReply["result"] = "error"; jsonReply["error"] = ex.Message; } return jsonReply; } } }