diff --git a/Tranga/Server.cs b/Tranga/Server.cs new file mode 100644 index 0000000..9052b9e --- /dev/null +++ b/Tranga/Server.cs @@ -0,0 +1,75 @@ +using System.Net; +using System.Runtime.InteropServices; +using System.Text; +using Newtonsoft.Json; + +namespace Tranga; + +public class Server : GlobalBase +{ + private readonly HttpListener _listener = new (); + private readonly Tranga _parent; + + public Server(Tranga parent) : base(parent) + { + this._parent = parent; + if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + this._listener.Prefixes.Add($"http://*:{settings.apiPortNumber}/"); + else + this._listener.Prefixes.Add($"http://localhost:{settings.apiPortNumber}/"); + Thread t = new (Listen); + t.Start(); + } + + private void Listen() + { + this._listener.Start(); + foreach(string prefix in this._listener.Prefixes) + Log($"Listening on {prefix}"); + while (this._listener.IsListening && _parent.keepRunning) + { + HttpListenerContext context = this._listener.GetContextAsync().Result; + Log($"{context.Request.HttpMethod} {context.Request.Url} {context.Request.UserAgent}"); + Task t = new(() => + { + if(context.Request.HttpMethod == "OPTIONS") + SendResponse(HttpStatusCode.OK, context.Response); + else + HandleRequest(context); + }); + t.Start(); + } + } + + private void HandleRequest(HttpListenerContext context) + { + HttpListenerRequest request = context.Request; + HttpListenerResponse response = context.Response; + if(request.Url!.LocalPath.Contains("favicon")) + SendResponse(HttpStatusCode.NoContent, response); + + SendResponse(HttpStatusCode.NotFound, response); + } + + private void SendResponse(HttpStatusCode statusCode, HttpListenerResponse response, object? content = null) + { + //logger?.WriteLine(this.GetType().ToString(), $"Sending response: {statusCode}"); + response.StatusCode = (int)statusCode; + response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); + response.AddHeader("Access-Control-Allow-Methods", "GET, POST, DELETE"); + response.AddHeader("Access-Control-Max-Age", "1728000"); + response.AppendHeader("Access-Control-Allow-Origin", "*"); + response.ContentType = "application/json"; + try + { + response.OutputStream.Write(content is not null + ? Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(content)) + : Array.Empty()); + response.OutputStream.Close(); + } + catch (HttpListenerException) + { + + } + } +} \ No newline at end of file diff --git a/Tranga/Tranga.cs b/Tranga/Tranga.cs new file mode 100644 index 0000000..05c90d5 --- /dev/null +++ b/Tranga/Tranga.cs @@ -0,0 +1,32 @@ +using Logging; +using Tranga.Jobs; + +namespace Tranga; + +public partial class Tranga : GlobalBase +{ + public bool keepRunning; + private JobBoss _jobBoss; + private Server server; + + public Tranga(Logger? logger, TrangaSettings settings) : base(logger, settings) + { + keepRunning = true; + _jobBoss = new(this); + StartJobBoss(); + this.server = new Server(this); + } + + private void StartJobBoss() + { + Thread t = new (() => + { + while (keepRunning) + { + _jobBoss.CheckJobs(); + Thread.Sleep(1000); + } + }); + t.Start(); + } +} \ No newline at end of file diff --git a/Tranga/TrangaArgs.cs b/Tranga/TrangaArgs.cs new file mode 100644 index 0000000..23be84e --- /dev/null +++ b/Tranga/TrangaArgs.cs @@ -0,0 +1,136 @@ +using Logging; + +namespace Tranga; + +public partial class Tranga : GlobalBase +{ + + public static void Main(string[] args) + { + string[]? help = GetArg(args, ArgEnum.Help); + if (help is not null) + { + PrintHelp(); + return; + } + + string[]? consoleLogger = GetArg(args, ArgEnum.ConsoleLogger); + string[]? fileLogger = GetArg(args, ArgEnum.FileLogger); + string? filePath = fileLogger?[0];//TODO validate path + + List enabledLoggers = new(); + if(consoleLogger is not null) + enabledLoggers.Add(Logger.LoggerType.ConsoleLogger); + if (fileLogger is not null) + enabledLoggers.Add(Logger.LoggerType.FileLogger); + Logger logger = new(enabledLoggers.ToArray(), Console.Out, Console.OutputEncoding, filePath); + + TrangaSettings? settings = null; + string[]? downloadLocationPath = GetArg(args, ArgEnum.DownloadLocation); + string[]? workingDirectory = GetArg(args, ArgEnum.WorkingDirectory); + + if (downloadLocationPath is not null && workingDirectory is not null) + { + settings = new TrangaSettings(downloadLocationPath[0], workingDirectory[0]); + }else if (downloadLocationPath is not null) + { + if (settings is null) + settings = new TrangaSettings(downloadLocation: downloadLocationPath[0]); + else + settings = new TrangaSettings(downloadLocation: downloadLocationPath[0], settings.workingDirectory); + }else if (workingDirectory is not null) + { + if (settings is null) + settings = new TrangaSettings(downloadLocation: workingDirectory[0]); + else + settings = new TrangaSettings(settings.downloadLocation, workingDirectory[0]); + } + else + { + settings = new TrangaSettings(); + } + + Directory.CreateDirectory(settings.downloadLocation);//TODO validate path + Directory.CreateDirectory(settings.workingDirectory);//TODO validate path + + Tranga _ = new (logger, settings); + } + + private static void PrintHelp() + { + Console.WriteLine("Tranga-Help:"); + foreach (Argument argument in arguments.Values) + { + foreach(string name in argument.names) + Console.Write("{0} ", name); + if(argument.parameterCount > 0) + Console.Write($"<{argument.parameterCount}>"); + Console.Write("\r\n {0}\r\n", argument.helpText); + } + } + + /// + /// Returns an array containing the parameters for the argument. + /// + /// List of argument-strings + /// Requested parameter + /// + /// If there are no parameters for an argument, returns an empty array. + /// If the argument is not found returns null. + /// + private static string[]? GetArg(string[] args, ArgEnum arg) + { + List argsList = args.ToList(); + List ret = new(); + foreach (string name in arguments[arg].names) + { + int argIndex = argsList.IndexOf(name); + if (argIndex != -1) + { + if (arguments[arg].parameterCount == 0) + return ret.ToArray(); + for (int parameterIndex = 1; parameterIndex <= arguments[arg].parameterCount; parameterIndex++) + { + if(argIndex + parameterIndex >= argsList.Count || args[argIndex + parameterIndex].Contains('-'))//End of arguments, or no parameter provided, when one is required + Console.WriteLine($"No parameter provided for argument {name}. -h for help."); + ret.Add(args[argIndex + parameterIndex]); + } + } + } + return ret.Any() ? ret.ToArray() : null; + } + + private static Dictionary arguments = new() + { + { ArgEnum.DownloadLocation, new(new []{"-d", "--downloadLocation"}, 1, "Directory to which downloaded Manga are saved") }, + { ArgEnum.WorkingDirectory, new(new []{"-w", "--workingDirectory"}, 1, "Directory in which application-data is saved") }, + { ArgEnum.ConsoleLogger, new(new []{"-c", "--consoleLogger"}, 0, "Enables the consoleLogger") }, + { ArgEnum.FileLogger, new(new []{"-f", "--fileLogger"}, 1, "Enables the fileLogger, Directory where logfiles are saved") }, + { ArgEnum.Help, new(new []{"-h", "--help"}, 0, "Print this") } + //{ ArgEnum., new(new []{""}, 1, "") } + }; + + internal enum ArgEnum + { + TrangaSettings, + DownloadLocation, + WorkingDirectory, + ConsoleLogger, + FileLogger, + Help + } + + private struct Argument + { + public string[] names { get; } + public byte parameterCount { get; } + public string helpText { get; } + + public Argument(string[] names, byte parameterCount, string helpText) + { + this.names = names; + this.parameterCount = parameterCount; + this.helpText = helpText; + } + } +} \ No newline at end of file