diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..83a9726 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# S4457: Parameter validation in "async"/"await" methods should be wrapped +dotnet_diagnostic.S4457.severity = silent diff --git a/HyperbolicDownloader.sln b/HyperbolicDownloader.sln index 4142730..6c5500c 100644 --- a/HyperbolicDownloader.sln +++ b/HyperbolicDownloader.sln @@ -3,9 +3,16 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32112.339 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HyperbolicDownloader", "HyperbolicDownloader\HyperbolicDownloader.csproj", "{7BA90CAF-36A1-4D55-9CE2-E498ECC1B62D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HyperbolicDownloaderCli", "HyperbolicDownloader\HyperbolicDownloaderCli.csproj", "{7BA90CAF-36A1-4D55-9CE2-E498ECC1B62D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HyperbolicDownloaderGUI", "HyperbolicDownloaderGUI\HyperbolicDownloaderGUI.csproj", "{E1CE6E07-F8A6-447F-837D-9D1943A42E96}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HyperbolicDownloaderGUI", "HyperbolicDownloaderGUI\HyperbolicDownloaderGUI.csproj", "{E1CE6E07-F8A6-447F-837D-9D1943A42E96}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HyperbolicDownloaderApi", "HyperbolicDownloaderApi\HyperbolicDownloaderApi.csproj", "{45ACEEC6-9AE4-4DA5-9A08-E22F1EBA7E22}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CA4F21C6-5B63-46C1-8576-91BB3FAE4FA7}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +28,10 @@ Global {E1CE6E07-F8A6-447F-837D-9D1943A42E96}.Debug|Any CPU.Build.0 = Debug|Any CPU {E1CE6E07-F8A6-447F-837D-9D1943A42E96}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1CE6E07-F8A6-447F-837D-9D1943A42E96}.Release|Any CPU.Build.0 = Release|Any CPU + {45ACEEC6-9AE4-4DA5-9A08-E22F1EBA7E22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45ACEEC6-9AE4-4DA5-9A08-E22F1EBA7E22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45ACEEC6-9AE4-4DA5-9A08-E22F1EBA7E22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45ACEEC6-9AE4-4DA5-9A08-E22F1EBA7E22}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/HyperbolicDownloader/HyperbolicDownloader.csproj b/HyperbolicDownloader/HyperbolicDownloaderCli.csproj similarity index 66% rename from HyperbolicDownloader/HyperbolicDownloader.csproj rename to HyperbolicDownloader/HyperbolicDownloaderCli.csproj index 3546f3f..1a90c33 100644 --- a/HyperbolicDownloader/HyperbolicDownloader.csproj +++ b/HyperbolicDownloader/HyperbolicDownloaderCli.csproj @@ -7,10 +7,17 @@ enable + + + + - + + + + diff --git a/HyperbolicDownloader/Program.cs b/HyperbolicDownloader/Program.cs index 81ca2b1..f9b5668 100644 --- a/HyperbolicDownloader/Program.cs +++ b/HyperbolicDownloader/Program.cs @@ -1,61 +1,42 @@ -using HyperbolicDownloader.FileProcessing; -using HyperbolicDownloader.Networking; -using HyperbolicDownloader.UserInterface; - -using Open.Nat; +using HyperbolicDownloaderApi.FileProcessing; +using HyperbolicDownloaderApi.Managment; +using HyperbolicDownloaderApi.Networking; +using HyperbolicDownloaderApi.UserInterface; using Stone_Red_Utilities.ConsoleExtentions; -using System.Diagnostics; -using System.Net; -using System.Net.Sockets; -using System.Reflection; using System.Text.Json; -namespace HyperbolicDownloader; +namespace HyperbolicDownloaderApi; internal static class Program { - public const int BroadcastPort = 2155; - - public static string BasePath { get; } = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location) ?? string.Empty; - public static string HostsFilePath { get; } = Path.Combine(BasePath, "Hosts.json"); - public static string FilesInfoPath { get; } = Path.Combine(BasePath, "Files.json"); - - public static int PublicPort { get; private set; } - public static int PrivatePort { get; } = 3055; - - public static IPAddress? PublicIpAddress => device?.GetExternalIPAsync().GetAwaiter().GetResult(); - - private static NatDevice? device; - private static Mapping? portMapping; - private static readonly HostsManager hostsManager = new(); - private static readonly FilesManager filesManager = new(); - private static readonly NetworkClient networkClient = new(filesManager); - private static readonly Random random = new(); + private static readonly ApiManager apiManager = new ApiManager(); private static async Task Main(string[] args) { Console.CancelKeyPress += Console_CancelKeyPress; Console.CursorVisible = false; - if (File.Exists(HostsFilePath)) + ApiManager.OnNotificationMessageRecived += ApiManager_OnNotificationMessageRecived; + + if (File.Exists(ApiConfiguration.HostsFilePath)) { - string hostsJson = await File.ReadAllTextAsync(HostsFilePath); - hostsManager.AddRange(JsonSerializer.Deserialize>(hostsJson) ?? new()); + string hostsJson = await File.ReadAllTextAsync(ApiConfiguration.HostsFilePath); + apiManager.HostsManager.AddRange(JsonSerializer.Deserialize>(hostsJson) ?? new()); } - if (File.Exists(FilesInfoPath)) + if (File.Exists(ApiConfiguration.FilesInfoPath)) { - string filesJson = await File.ReadAllTextAsync(FilesInfoPath); - filesManager.AddRange(JsonSerializer.Deserialize>(filesJson) ?? new()); + string filesJson = await File.ReadAllTextAsync(ApiConfiguration.FilesInfoPath); + apiManager.FilesManager.AddRange(JsonSerializer.Deserialize>(filesJson) ?? new()); } - InputHandler inputHandler = new InputHandler(hostsManager, filesManager); + InputHandler inputHandler = new InputHandler(apiManager.HostsManager, apiManager.FilesManager); if (args.Length > 0 && File.Exists(args[0])) { - UserInterface.Commands.DownloadCommands downloadCommands = new UserInterface.Commands.DownloadCommands(hostsManager, filesManager); + Commands.DownloadCommands downloadCommands = new Commands.DownloadCommands(apiManager.HostsManager, apiManager.FilesManager); downloadCommands.GetFileFrom(args[0]); Console.WriteLine("Do you want to continue using this instance? [y/N]"); if (char.ToLower(Console.ReadKey().KeyChar) != 'y') @@ -66,211 +47,53 @@ internal static class Program } Console.WriteLine("Searching for a UPnP/NAT-PMP device..."); - _ = await OpenPorts(); + _ = await ApiManager.OpenPorts(); ConsoleExt.WriteLine($"The private IP address is: {NetworkUtilities.GetIP4Adress()} ", ConsoleColor.Green); - ConsoleExt.WriteLine($"The private port is: {PrivatePort}", ConsoleColor.Green); + ConsoleExt.WriteLine($"The private port is: {ApiConfiguration.PrivatePort}", ConsoleColor.Green); Console.WriteLine("Starting TCP listener..."); + apiManager.StartTcpListener(); - try - { - networkClient.ListenTo("GetHostsList", GetHostList); - networkClient.ListenTo>("DiscoverAnswer", DiscoverAnswer); - networkClient.ListenTo("Message", ReciveMessage); - networkClient.ListenTo("HasFile", HasFile); - networkClient.StartListening(PrivatePort); - } - catch (SocketException ex) - { - ConsoleExt.WriteLine($"An error occurred while starting the TCP listener! Error message: {ex.Message}", ConsoleColor.Red); // net stop hns && net start hns - Console.ReadKey(); - return; - } - - BroadcastClient broadcastClient = new BroadcastClient(); - - Console.WriteLine("Running local discovery routine..."); - BroadcastClient.Send(BroadcastPort, PrivatePort.ToString()); - await Task.Delay(3000); + Console.WriteLine("Starting broadcast listener..."); + apiManager.StartBroadcastListener(); int activeHostsCount = 0; - if (hostsManager.Count > 0) + if (apiManager.HostsManager.Count > 0) { Console.WriteLine("Checking if hosts are active..."); - activeHostsCount = hostsManager.CheckHostsActivity(); + activeHostsCount = apiManager.HostsManager.CheckHostsActivity(); } if (activeHostsCount == 0) { ConsoleExt.WriteLine("No active hosts found!", ConsoleColor.Red); - ConsoleExt.WriteLine("Use 'add host xxx.xxx.xxx.xxx:yyyy' to add a new host.", ConsoleColor.Red); + ConsoleExt.WriteLine("Use 'add host xxx.xxx.xxx.xxx:yyyy' to add a new host or use 'discover' to find hosts in the local network.", ConsoleColor.Red); } - Console.WriteLine($"{hostsManager.Count} known host(s)."); + Console.WriteLine($"{apiManager.HostsManager.Count} known host(s)."); Console.WriteLine($"{activeHostsCount} active host(s)."); - Console.WriteLine("Starting broadcast listener..."); - broadcastClient.StartListening(BroadcastPort); - broadcastClient.OnBroadcastRecived += BroadcastClient_OnBroadcastRecived; - ConsoleExt.WriteLine("Done", ConsoleColor.Green); + + ConsoleExt.WriteLine("Ready", ConsoleColor.Green); inputHandler.ReadInput(); - ClosePorts(); + ApiManager.ClosePorts(); } - private static async void BroadcastClient_OnBroadcastRecived(object? sender, BroadcastRecivedEventArgs recivedEventArgs) + private static void ApiManager_OnNotificationMessageRecived(object? sender, NotificationMessageEventArgs e) { - Debug.WriteLine($"Received broadcast \"{recivedEventArgs.Message}\" from {recivedEventArgs.IPEndPoint.Address}"); - List hostsToSend = hostsManager.ToList(); - - NetworkSocket? localSocket = GetLocalSocket(); - - if (localSocket is null) + switch (e.NotificationMessageType) { - return; + case NotificationMessageType.Raw: Console.Write(e.Message); break; + case NotificationMessageType.Success: ConsoleExt.Write(e.Message, ConsoleColor.Green); break; + case NotificationMessageType.Warning: ConsoleExt.Write(e.Message, ConsoleColor.DarkYellow); break; + case NotificationMessageType.Error: ConsoleExt.Write(e.Message, ConsoleColor.Red); break; } - - hostsToSend.RemoveAll(x => x.IPAddress == recivedEventArgs.IPEndPoint.Address.ToString()); - hostsToSend.Add(localSocket); - - bool success = int.TryParse(recivedEventArgs.Message, out int remotePort); - - if (success) - { - hostsManager.Add(new NetworkSocket(recivedEventArgs.IPEndPoint.Address.ToString(), remotePort, DateTime.Now)); - try - { - await NetworkClient.SendAsync(recivedEventArgs.IPEndPoint.Address, remotePort, "DiscoverAnswer", hostsToSend); - } - catch (SocketException ex) - { - Debug.WriteLine(ex); - } - } - } - - private static void DiscoverAnswer(object? sender, MessageRecivedEventArgs> recivedEventArgs) - { - Console.WriteLine($"Received answer from {recivedEventArgs.IpAddress}. Returned {recivedEventArgs.Data.Count} host(s)."); - hostsManager.AddRange(recivedEventArgs.Data); - } - - private static void ReciveMessage(object? sender, MessageRecivedEventArgs recivedEventArgs) - { - Console.WriteLine($"Received \"{recivedEventArgs.Data}\" from {recivedEventArgs.IpAddress}."); - } - - private static async void GetHostList(object? sender, MessageRecivedEventArgs recivedEventArgs) - { - List hostsToSend = hostsManager.ToList(); - - NetworkSocket? localSocket = GetLocalSocket(); - - if (localSocket is null) - { - return; - } - - hostsToSend.RemoveAll(x => x.IPAddress == recivedEventArgs.IpAddress.ToString()); - hostsToSend.Add(localSocket); - - if (recivedEventArgs.Data.Port != 0) - { - hostsManager.Add(recivedEventArgs.Data); - } - - await recivedEventArgs.SendResponseAsync(hostsToSend); - } - - private static async void HasFile(object? sender, MessageRecivedEventArgs recivedEventArgs) - { - await recivedEventArgs.SendResponseAsync(filesManager.Contains(recivedEventArgs.Data)); - } - - public static async Task OpenPorts() - { - try - { - PublicPort = random.Next(1000, 6000); - - NatDiscoverer? discoverer = new NatDiscoverer(); - device = await discoverer.DiscoverDeviceAsync(); - - IPAddress? ip = await device.GetExternalIPAsync(); - ConsoleExt.WriteLine($"The public IP address is: {ip} ", ConsoleColor.Green); - - portMapping = new Mapping(Protocol.Tcp, PrivatePort, PublicPort, "HyperbolicDowloader"); - - await device.CreatePortMapAsync(portMapping); - - ConsoleExt.WriteLine($"The public port is: {PublicPort}", ConsoleColor.Green); - return true; - } - catch (NatDeviceNotFoundException) - { - ConsoleExt.WriteLine($"Could not find a UPnP or NAT-PMP device!", ConsoleColor.Red); - return false; - } - catch (MappingException ex) - { - ConsoleExt.WriteLine($"An error occurred while mapping the private port ({PrivatePort}) to the public port ({PublicPort})! Error message: {ex.Message}", ConsoleColor.Red); - return false; - } - } - - private static void ClosePorts() - { - Console.WriteLine("Closing ports..."); - if (device is not null) - { - try - { - IEnumerable? mappings = device.GetAllMappingsAsync().GetAwaiter().GetResult(); - foreach (Mapping? mapping in mappings) - { - if (mapping.Description.Contains("HyperbolicDowloader") && mapping.PrivateIP.ToString() == portMapping?.PrivateIP.ToString()) - { - device.DeletePortMapAsync(mapping).GetAwaiter().GetResult(); - } - } - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - } - - ConsoleExt.WriteLine("Ports closed!", ConsoleColor.DarkYellow); - Environment.Exit(0); } private static void Console_CancelKeyPress(object? sender, ConsoleCancelEventArgs e) { - ClosePorts(); - hostsManager.SaveHosts(); - } - - public static NetworkSocket? GetLocalSocket() - { - int port = PublicPort; - string? ipAddress = null; - - if (device is not null) - { - ipAddress = (device.GetExternalIPAsync()).GetAwaiter().GetResult()?.ToString(); - } - - if (ipAddress is null || ipAddress == "0.0.0.0") - { - ipAddress = NetworkUtilities.GetIP4Adress()?.ToString(); - port = PrivatePort; - } - - if (ipAddress is null) - { - return null; - } - - return new NetworkSocket(ipAddress, port, DateTime.Now); + ApiManager.ClosePorts(); + apiManager.HostsManager.SaveHosts(); } } \ No newline at end of file diff --git a/HyperbolicDownloader/UserInterface/Commands/ClientCommands.cs b/HyperbolicDownloader/UserInterface/Commands/ClientCommands.cs deleted file mode 100644 index 35b659b..0000000 --- a/HyperbolicDownloader/UserInterface/Commands/ClientCommands.cs +++ /dev/null @@ -1,21 +0,0 @@ -using HyperbolicDownloader.Networking; - -using Stone_Red_Utilities.ConsoleExtentions; - -namespace HyperbolicDownloader.UserInterface.Commands; - -internal class ClientCommands -{ - public void ShowInfo(string _) - { - if (Program.PublicIpAddress is not null) - { - ConsoleExt.WriteLine($"The public IP address is: {Program.PublicIpAddress}", ConsoleColor.Green); - ConsoleExt.WriteLine($"The public port is: {Program.PublicPort}", ConsoleColor.Green); - Console.WriteLine(); - } - - ConsoleExt.WriteLine($"The private IP address is: {NetworkUtilities.GetIP4Adress()}", ConsoleColor.Green); - ConsoleExt.WriteLine($"The private port is: {Program.PrivatePort}", ConsoleColor.Green); - } -} \ No newline at end of file diff --git a/HyperbolicDownloader/UserInterface/Commands/HostCommands.cs b/HyperbolicDownloader/UserInterface/Commands/HostCommands.cs deleted file mode 100644 index 12a0a8d..0000000 --- a/HyperbolicDownloader/UserInterface/Commands/HostCommands.cs +++ /dev/null @@ -1,149 +0,0 @@ -using HyperbolicDownloader.Networking; - -using Stone_Red_Utilities.ConsoleExtentions; - -using System.Net; -using System.Net.Sockets; - -namespace HyperbolicDownloader.UserInterface.Commands; - -internal class HostCommands -{ - private readonly HostsManager hostsManager; - - public HostCommands(HostsManager hostsManager) - { - this.hostsManager = hostsManager; - } - - public void Discover(string _) - { - Console.WriteLine("Running local discovery routine..."); - BroadcastClient.Send(Program.BroadcastPort, Program.PrivatePort.ToString()); - Thread.Sleep(3000); - } - - public void CheckActiveHosts(string _) - { - int activeHostsCount = hostsManager.CheckHostsActivity(); - if (activeHostsCount == 0) - { - ConsoleExt.WriteLine("No active hosts found!", ConsoleColor.Red); - ConsoleExt.WriteLine("Use 'add host xxx.xxx.xxx.xxx:yyyy' to add a new host.", ConsoleColor.Red); - } - - Console.WriteLine(); - Console.WriteLine($"{hostsManager.Count} known host(s)."); - Console.WriteLine($"{activeHostsCount} active host(s)."); - } - - public void ListHosts(string _) - { - int index = 0; - List hosts = hostsManager.ToList(); - - if (hosts.Count == 0) - { - ConsoleExt.WriteLine("No known hosts", ConsoleColor.DarkYellow); - return; - } - - foreach (NetworkSocket host in hosts) - { - index++; - Console.WriteLine($"{index}) {host.IPAddress}:{host.Port}"); - Console.WriteLine($"Last active: {host.LastActive}"); - Console.WriteLine(); - } - Console.CursorTop--; - } - - public void RemoveHost(string args) - { - string[] parts = args.Split(":"); - - if (parts.Length != 2) - { - ConsoleExt.WriteLine("Invalid format! Use this format: (xxx.xxx.xxx.xxx:yyyy)", ConsoleColor.Red); - return; - } - - string ipAddressInput = parts[0]; - string portInput = parts[1]; - - _ = int.TryParse(portInput, out int port); - if (port < 1000 || port >= 6000) - { - ConsoleExt.WriteLine("Invalid port number!", ConsoleColor.Red); - return; - } - - if (!IPAddress.TryParse(ipAddressInput, out IPAddress? ipAddress)) - { - ConsoleExt.WriteLine("Invalid IP address!", ConsoleColor.Red); - return; - } - - NetworkSocket hostToRemove = new NetworkSocket(ipAddress.ToString(), port, DateTime.MinValue); - - if (!hostsManager.Contains(hostToRemove)) - { - ConsoleExt.WriteLine("Host not in list", ConsoleColor.Red); - return; - } - - hostsManager.Remove(new NetworkSocket(ipAddress.ToString(), port, DateTime.MinValue), true); - ConsoleExt.WriteLine($"Successfully Removed host!", ConsoleColor.Green); - } - - public void AddHost(string args) - { - string[] parts = args.Split(":"); - - if (parts.Length != 2) - { - ConsoleExt.WriteLine("Invalid format! Use this format: (xxx.xxx.xxx.xxx:yyyy)", ConsoleColor.Red); - return; - } - - string ipAddressInput = parts[0]; - string portInput = parts[1]; - - _ = int.TryParse(portInput, out int port); - if (port < 1000 || port >= 6000) - { - ConsoleExt.WriteLine("Invalid port number!", ConsoleColor.Red); - } - else if (IPAddress.TryParse(ipAddressInput, out IPAddress? ipAddress)) - { - try - { - Console.WriteLine("Waiting for response..."); - NetworkSocket? localSocket = Program.GetLocalSocket() ?? new NetworkSocket("0.0.0.0", 0, DateTime.MinValue); - List? recivedHosts = NetworkClient.Send>(ipAddress, port, "GetHostsList", localSocket); - - if (recivedHosts is not null) - { - ConsoleExt.WriteLine($"Success! Added {recivedHosts.Count} new host(s).", ConsoleColor.Green); - hostsManager.AddRange(recivedHosts); - } - else - { - ConsoleExt.WriteLine($"Invalid response!", ConsoleColor.Red); - } - } - catch (SocketException ex) - { - ConsoleExt.WriteLine($"Invalid host! Error message: {ex.Message}", ConsoleColor.Red); - } - catch (IOException ex) - { - ConsoleExt.WriteLine($"Invalid host! Error message: {ex.Message}", ConsoleColor.Red); - } - } - else - { - ConsoleExt.WriteLine("Invalid IP address!", ConsoleColor.Red); - } - } -} \ No newline at end of file diff --git a/HyperbolicDownloader/UserInterface/InputHandler.cs b/HyperbolicDownloader/UserInterface/InputHandler.cs index 43e438e..acd0ea6 100644 --- a/HyperbolicDownloader/UserInterface/InputHandler.cs +++ b/HyperbolicDownloader/UserInterface/InputHandler.cs @@ -1,11 +1,11 @@ using Commander_Net; -using HyperbolicDownloader.FileProcessing; -using HyperbolicDownloader.UserInterface.Commands; +using HyperbolicDownloaderApi.Commands; +using HyperbolicDownloaderApi.FileProcessing; using Stone_Red_Utilities.ConsoleExtentions; -namespace HyperbolicDownloader.UserInterface; +namespace HyperbolicDownloaderApi.UserInterface; internal class InputHandler { diff --git a/HyperbolicDownloaderApi/Commands/ClientCommands.cs b/HyperbolicDownloaderApi/Commands/ClientCommands.cs new file mode 100644 index 0000000..425dae6 --- /dev/null +++ b/HyperbolicDownloaderApi/Commands/ClientCommands.cs @@ -0,0 +1,20 @@ +using HyperbolicDownloaderApi.Managment; +using HyperbolicDownloaderApi.Networking; + +namespace HyperbolicDownloaderApi.Commands; + +public class ClientCommands +{ + public void ShowInfo(string _) + { + if (ApiManager.PublicIpAddress is not null) + { + ApiManager.SendMessageNewLine($"The public IP address is: {ApiManager.PublicIpAddress}", NotificationMessageType.Success); + ApiManager.SendMessageNewLine($"The public port is: {ApiConfiguration.PublicPort}", NotificationMessageType.Success); + ApiManager.SendMessageNewLine(string.Empty, NotificationMessageType.Raw); + } + + ApiManager.SendMessageNewLine($"The private IP address is: {NetworkUtilities.GetIP4Adress()}", NotificationMessageType.Success); + ApiManager.SendMessageNewLine($"The private port is: {ApiConfiguration.PrivatePort}", NotificationMessageType.Success); + } +} \ No newline at end of file diff --git a/HyperbolicDownloader/UserInterface/Commands/DownloadCommands.cs b/HyperbolicDownloaderApi/Commands/DownloadCommands.cs similarity index 67% rename from HyperbolicDownloader/UserInterface/Commands/DownloadCommands.cs rename to HyperbolicDownloaderApi/Commands/DownloadCommands.cs index ca3ee90..15d623f 100644 --- a/HyperbolicDownloader/UserInterface/Commands/DownloadCommands.cs +++ b/HyperbolicDownloaderApi/Commands/DownloadCommands.cs @@ -1,7 +1,7 @@ -using HyperbolicDownloader.FileProcessing; -using HyperbolicDownloader.Networking; +using HyperbolicDownloaderApi.FileProcessing; +using HyperbolicDownloaderApi.Managment; +using HyperbolicDownloaderApi.Networking; -using Stone_Red_Utilities.ConsoleExtentions; using Stone_Red_Utilities.StringExtentions; using System.Diagnostics; @@ -10,9 +10,9 @@ using System.Net.Sockets; using System.Text; using System.Text.Json; -namespace HyperbolicDownloader.UserInterface.Commands; +namespace HyperbolicDownloaderApi.Commands; -internal class DownloadCommands +public class DownloadCommands { private readonly HostsManager hostsManager; private readonly FilesManager filesManager; @@ -27,7 +27,7 @@ internal class DownloadCommands { if (string.IsNullOrWhiteSpace(path)) { - ConsoleExt.WriteLine("Path is empty!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("Path is empty!", NotificationMessageType.Error); return; } @@ -35,7 +35,7 @@ internal class DownloadCommands if (!File.Exists(fullPath)) { - ConsoleExt.WriteLine("Invalid file path!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("Invalid file path!", NotificationMessageType.Error); } string json = File.ReadAllText(fullPath); @@ -43,7 +43,7 @@ internal class DownloadCommands PublicHyperFileInfo? publicHyperFileInfo = JsonSerializer.Deserialize(json); if (publicHyperFileInfo == null) { - ConsoleExt.WriteLine("Parsing file failed!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("Parsing file failed!", NotificationMessageType.Error); return; } @@ -55,7 +55,7 @@ internal class DownloadCommands { if (string.IsNullOrEmpty(hash)) { - ConsoleExt.WriteLine("No hash value specified!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("No hash value specified!", NotificationMessageType.Error); return; } @@ -71,7 +71,7 @@ internal class DownloadCommands continue; } - ConsoleExt.Write($"{host.IPAddress}:{host.Port} > ???", ConsoleColor.DarkYellow); + ApiManager.SendMessage($"{host.IPAddress}:{host.Port} > ???", NotificationMessageType.Warning); Console.CursorLeft = 0; @@ -82,7 +82,7 @@ internal class DownloadCommands if (!sendTask.IsCompletedSuccessfully) { Console.CursorLeft = 0; - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Inactive", ConsoleColor.Red); + ApiManager.SendMessageNewLine($"{host.IPAddress}:{host.Port} > Inactive", NotificationMessageType.Error); hostsManager.Remove(host); continue; @@ -90,14 +90,14 @@ internal class DownloadCommands else if (!sendTask.Result) { host.LastActive = DateTime.Now; - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Does not have the requested file", ConsoleColor.Red); + ApiManager.SendMessageNewLine($"{host.IPAddress}:{host.Port} > Does not have the requested file", NotificationMessageType.Error); continue; } host.LastActive = DateTime.Now; - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Has the requested file", ConsoleColor.Green); - Console.WriteLine("Requesting file..."); + ApiManager.SendMessageNewLine($"{host.IPAddress}:{host.Port} > Has the requested file", NotificationMessageType.Success); + ApiManager.SendMessageNewLine("Requesting file..."); using TcpClient tcpClient = new TcpClient(); tcpClient.Connect(ipAddress!, host.Port); @@ -118,8 +118,8 @@ internal class DownloadCommands } catch (IOException) { - Console.WriteLine(); - ConsoleExt.WriteLine("Lost connection to other host!", ConsoleColor.Red); + ApiManager.SendMessageNewLine(string.Empty); + ApiManager.SendMessageNewLine("Lost connection to other host!", NotificationMessageType.Error); continue; } @@ -133,16 +133,16 @@ internal class DownloadCommands if (!validFileSize || fileSize <= 0) { - ConsoleExt.WriteLine("Invalid file size!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("Invalid file size!", NotificationMessageType.Error); continue; } string fileName = parts[1].ToFileName(); - string directoryPath = Path.Combine(Program.BasePath, "Downloads"); + string directoryPath = Path.Combine(ApiConfiguration.BasePath, "Downloads"); string filePath = Path.Combine(directoryPath, fileName); - Console.WriteLine($"File name: {fileName}"); - Console.WriteLine($"Starting download..."); + ApiManager.SendMessageNewLine($"File name: {fileName}"); + ApiManager.SendMessageNewLine($"Starting download..."); int totalBytesRead = 0; @@ -167,8 +167,8 @@ internal class DownloadCommands } catch (IOException) { - Console.WriteLine(); - ConsoleExt.WriteLine("Lost connection to other host!", ConsoleColor.Red); + ApiManager.SendMessageNewLine(string.Empty); + ApiManager.SendMessageNewLine("Lost connection to other host!", NotificationMessageType.Error); break; } @@ -196,9 +196,7 @@ internal class DownloadCommands stopWatch.Restart(); } - Console.CursorLeft = 0; - - Console.Out.WriteAsync($"Downloading: {Math.Clamp(Math.Ceiling(100d / fileSize * totalBytesRead), 0, 100)}% {totalBytesRead / 1000}/{fileSize / 1000}KB [{unitsPerSecond}{unit}/s] "); + ApiManager.SendMessage($"\rDownloading: {Math.Clamp(Math.Ceiling(100d / fileSize * totalBytesRead), 0, 100)}% {totalBytesRead / 1000}/{fileSize / 1000}KB [{unitsPerSecond}{unit}/s] "); } fileStream.Close(); @@ -208,30 +206,30 @@ internal class DownloadCommands continue; } - Console.WriteLine(); + ApiManager.SendMessageNewLine(string.Empty); - Console.WriteLine("Validating file..."); + ApiManager.SendMessageNewLine("Validating file..."); if (FileValidator.ValidateHash(filePath, hash)) { _ = filesManager.TryAdd(filePath, out _, out _); } else { - ConsoleExt.WriteLine("Warning: File hash does not match! File might me corrupted or manipulated!", ConsoleColor.DarkYellow); + ApiManager.SendMessageNewLine("Warning: File hash does not match! File might me corrupted or manipulated!", NotificationMessageType.Warning); } - Console.WriteLine($"File saved at: {Path.GetFullPath(filePath)}"); - ConsoleExt.WriteLine("Done", ConsoleColor.Green); + ApiManager.SendMessageNewLine($"File saved at: {Path.GetFullPath(filePath)}"); + ApiManager.SendMessageNewLine("Done", NotificationMessageType.Success); stopWatch.Stop(); hostsManager.SaveHosts(); return; } else { - ConsoleExt.WriteLine(dataReceived, ConsoleColor.Red); + ApiManager.SendMessageNewLine(dataReceived, NotificationMessageType.Error); } } - ConsoleExt.WriteLine("None of the available hosts have the requested file!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("None of the available hosts have the requested file!", NotificationMessageType.Error); hostsManager.SaveHosts(); } } \ No newline at end of file diff --git a/HyperbolicDownloader/UserInterface/Commands/FileCommands.cs b/HyperbolicDownloaderApi/Commands/FileCommands.cs similarity index 60% rename from HyperbolicDownloader/UserInterface/Commands/FileCommands.cs rename to HyperbolicDownloaderApi/Commands/FileCommands.cs index 2d64795..8586f7a 100644 --- a/HyperbolicDownloader/UserInterface/Commands/FileCommands.cs +++ b/HyperbolicDownloaderApi/Commands/FileCommands.cs @@ -1,14 +1,13 @@ -using HyperbolicDownloader.FileProcessing; -using HyperbolicDownloader.Networking; - -using Stone_Red_Utilities.ConsoleExtentions; +using HyperbolicDownloaderApi.FileProcessing; +using HyperbolicDownloaderApi.Managment; +using HyperbolicDownloaderApi.Networking; using System.Net; using System.Text.Json; -namespace HyperbolicDownloader.UserInterface.Commands; +namespace HyperbolicDownloaderApi.Commands; -internal class FileCommands +public class FileCommands { private readonly HostsManager hostsManager; @@ -24,12 +23,12 @@ internal class FileCommands { if (filesManager.TryAdd(path, out PrivateHyperFileInfo? fileInfo, out string? message)) { - ConsoleExt.WriteLine($"Added file: {fileInfo!.FilePath}", ConsoleColor.Green); - Console.WriteLine($"Hash: {fileInfo.Hash}"); + ApiManager.SendMessageNewLine($"Added file: {fileInfo!.FilePath}", NotificationMessageType.Success); + ApiManager.SendMessageNewLine($"Hash: {fileInfo.Hash}"); } else { - ConsoleExt.WriteLine(message, ConsoleColor.Red); + ApiManager.SendMessageNewLine(message, NotificationMessageType.Error); } } @@ -39,11 +38,11 @@ internal class FileCommands if (filesManager.TryRemove(hash)) { - ConsoleExt.WriteLine($"Successfully removed file!", ConsoleColor.Green); + ApiManager.SendMessageNewLine($"Successfully removed file!", NotificationMessageType.Success); } else { - ConsoleExt.WriteLine("The file is not being tracked!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("The file is not being tracked!", NotificationMessageType.Error); } } @@ -54,23 +53,23 @@ internal class FileCommands if (fileInfos.Count == 0) { - ConsoleExt.WriteLine("No tracked files!", ConsoleColor.DarkYellow); + ApiManager.SendMessageNewLine("No tracked files!", NotificationMessageType.Warning); return; } foreach (PrivateHyperFileInfo fileInfo in fileInfos) { index++; - Console.WriteLine($"{index}) {fileInfo.FilePath}"); - Console.WriteLine($"Hash: {fileInfo.Hash}"); - Console.WriteLine(); + ApiManager.SendMessageNewLine($"{index}) {fileInfo.FilePath}"); + ApiManager.SendMessageNewLine($"Hash: {fileInfo.Hash}"); + ApiManager.SendMessageNewLine(string.Empty); } Console.CursorTop--; } public void GenerateFileSingle(string hash) { - string directoryPath = Path.Combine(Program.BasePath, "GeneratedFiles"); + string directoryPath = Path.Combine(ApiConfiguration.BasePath, "GeneratedFiles"); if (!Directory.Exists(directoryPath)) { Directory.CreateDirectory(directoryPath); @@ -80,7 +79,7 @@ internal class FileCommands if (!filesManager.TryGet(hash, out PrivateHyperFileInfo? localHyperFileInfo)) { - ConsoleExt.WriteLine("The file is not being tracked!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("The file is not being tracked!", NotificationMessageType.Error); return; } @@ -89,10 +88,10 @@ internal class FileCommands PublicHyperFileInfo publicHyperFileInfo = new PublicHyperFileInfo(hash); - NetworkSocket? localHost = Program.GetLocalSocket(); + NetworkSocket? localHost = ApiManager.GetLocalSocket(); if (localHost is null) { - ConsoleExt.WriteLine("Network error!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("Network error!", NotificationMessageType.Error); return; } @@ -103,13 +102,13 @@ internal class FileCommands File.WriteAllText(filePath, json); - ConsoleExt.WriteLine("Done", ConsoleColor.Green); - Console.WriteLine($"File saved at: {Path.GetFullPath(filePath)}"); + ApiManager.SendMessageNewLine("Done", NotificationMessageType.Success); + ApiManager.SendMessageNewLine($"File saved at: {Path.GetFullPath(filePath)}"); } public void GenerateFileFull(string hash) { - string directoryPath = Path.Combine(Program.BasePath, "GeneratedFiles"); + string directoryPath = Path.Combine(ApiConfiguration.BasePath, "GeneratedFiles"); if (!Directory.Exists(directoryPath)) { Directory.CreateDirectory(directoryPath); @@ -119,7 +118,7 @@ internal class FileCommands if (!filesManager.TryGet(hash, out PrivateHyperFileInfo? localHyperFileInfo)) { - ConsoleExt.WriteLine("The file is not being tracked!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("The file is not being tracked!", NotificationMessageType.Error); return; } @@ -128,10 +127,10 @@ internal class FileCommands PublicHyperFileInfo publicHyperFileInfo = new PublicHyperFileInfo(hash); - NetworkSocket? localHost = Program.GetLocalSocket(); + NetworkSocket? localHost = ApiManager.GetLocalSocket(); if (localHost is null) { - ConsoleExt.WriteLine("Network error!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("Network error!", NotificationMessageType.Error); return; } @@ -145,7 +144,7 @@ internal class FileCommands continue; } - ConsoleExt.Write($"{host.IPAddress}:{host.Port} > ???", ConsoleColor.DarkYellow); + ApiManager.SendMessage($"{host.IPAddress}:{host.Port} > ???", NotificationMessageType.Warning); Console.CursorLeft = 0; @@ -156,7 +155,7 @@ internal class FileCommands if (!sendTask.IsCompletedSuccessfully) { Console.CursorLeft = 0; - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Inactive", ConsoleColor.Red); + ApiManager.SendMessageNewLine($"{host.IPAddress}:{host.Port} > Inactive", NotificationMessageType.Error); hostsManager.Remove(host); continue; @@ -164,13 +163,13 @@ internal class FileCommands else if (!sendTask.Result) { host.LastActive = DateTime.Now; - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Does not have the requested file", ConsoleColor.Red); + ApiManager.SendMessageNewLine($"{host.IPAddress}:{host.Port} > Does not have the requested file", NotificationMessageType.Error); continue; } host.LastActive = DateTime.Now; - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Has the requested file", ConsoleColor.Green); + ApiManager.SendMessageNewLine($"{host.IPAddress}:{host.Port} > Has the requested file", NotificationMessageType.Success); publicHyperFileInfo.Hosts.Add(host); } @@ -181,7 +180,7 @@ internal class FileCommands File.WriteAllText(filePath, json); - ConsoleExt.WriteLine("Done", ConsoleColor.Green); - Console.WriteLine($"File saved at: {Path.GetFullPath(filePath)}"); + ApiManager.SendMessageNewLine("Done", NotificationMessageType.Success); + ApiManager.SendMessageNewLine($"File saved at: {Path.GetFullPath(filePath)}"); } } \ No newline at end of file diff --git a/HyperbolicDownloaderApi/Commands/HostCommands.cs b/HyperbolicDownloaderApi/Commands/HostCommands.cs new file mode 100644 index 0000000..9cbfaea --- /dev/null +++ b/HyperbolicDownloaderApi/Commands/HostCommands.cs @@ -0,0 +1,148 @@ +using HyperbolicDownloaderApi.Managment; +using HyperbolicDownloaderApi.Networking; + +using System.Net; +using System.Net.Sockets; + +namespace HyperbolicDownloaderApi.Commands; + +public class HostCommands +{ + private readonly HostsManager hostsManager; + + public HostCommands(HostsManager hostsManager) + { + this.hostsManager = hostsManager; + } + + public void Discover(string _) + { + ApiManager.SendMessageNewLine("Running local discovery routine...", NotificationMessageType.Raw); + BroadcastClient.Send(ApiConfiguration.BroadcastPort, ApiConfiguration.PrivatePort.ToString()); + Thread.Sleep(3000); + } + + public void CheckActiveHosts(string _) + { + int activeHostsCount = hostsManager.CheckHostsActivity(); + if (activeHostsCount == 0) + { + ApiManager.SendMessageNewLine("No active hosts found!", NotificationMessageType.Error); + ApiManager.SendMessageNewLine("Use 'add host xxx.xxx.xxx.xxx:yyyy' to add a new host.", NotificationMessageType.Error); + } + + ApiManager.SendMessageNewLine(string.Empty, NotificationMessageType.Raw); + ApiManager.SendMessageNewLine($"{hostsManager.Count} known host(s).", NotificationMessageType.Raw); + ApiManager.SendMessageNewLine($"{activeHostsCount} active host(s).", NotificationMessageType.Raw); + } + + public void ListHosts(string _) + { + int index = 0; + List hosts = hostsManager.ToList(); + + if (hosts.Count == 0) + { + ApiManager.SendMessageNewLine("No known hosts", NotificationMessageType.Warning); + return; + } + + foreach (NetworkSocket host in hosts) + { + index++; + ApiManager.SendMessageNewLine($"{index}) {host.IPAddress}:{host.Port}", NotificationMessageType.Raw); + ApiManager.SendMessageNewLine($"Last active: {host.LastActive}", NotificationMessageType.Raw); + ApiManager.SendMessageNewLine(string.Empty, NotificationMessageType.Raw); + } + Console.CursorTop--; + } + + public void RemoveHost(string args) + { + string[] parts = args.Split(":"); + + if (parts.Length != 2) + { + ApiManager.SendMessageNewLine("Invalid format! Use this format: (xxx.xxx.xxx.xxx:yyyy)", NotificationMessageType.Error); + return; + } + + string ipAddressInput = parts[0]; + string portInput = parts[1]; + + _ = int.TryParse(portInput, out int port); + if (port < 1000 || port >= 6000) + { + ApiManager.SendMessageNewLine("Invalid port number!", NotificationMessageType.Error); + return; + } + + if (!IPAddress.TryParse(ipAddressInput, out IPAddress? ipAddress)) + { + ApiManager.SendMessageNewLine("Invalid IP address!", NotificationMessageType.Error); + return; + } + + NetworkSocket hostToRemove = new NetworkSocket(ipAddress.ToString(), port, DateTime.MinValue); + + if (!hostsManager.Contains(hostToRemove)) + { + ApiManager.SendMessageNewLine("Host not in list", NotificationMessageType.Error); + return; + } + + hostsManager.Remove(new NetworkSocket(ipAddress.ToString(), port, DateTime.MinValue), true); + ApiManager.SendMessageNewLine($"Successfully Removed host!", NotificationMessageType.Success); + } + + public void AddHost(string args) + { + string[] parts = args.Split(":"); + + if (parts.Length != 2) + { + ApiManager.SendMessageNewLine("Invalid format! Use this format: (xxx.xxx.xxx.xxx:yyyy)", NotificationMessageType.Error); + return; + } + + string ipAddressInput = parts[0]; + string portInput = parts[1]; + + _ = int.TryParse(portInput, out int port); + if (port < 1000 || port >= 6000) + { + ApiManager.SendMessageNewLine("Invalid port number!", NotificationMessageType.Error); + } + else if (IPAddress.TryParse(ipAddressInput, out IPAddress? ipAddress)) + { + try + { + ApiManager.SendMessageNewLine("Waiting for response...", NotificationMessageType.Raw); + NetworkSocket? localSocket = ApiManager.GetLocalSocket() ?? new NetworkSocket("0.0.0.0", 0, DateTime.MinValue); + List? recivedHosts = NetworkClient.Send>(ipAddress, port, "GetHostsList", localSocket); + + if (recivedHosts is not null) + { + ApiManager.SendMessageNewLine($"Success! Added {recivedHosts.Count} new host(s).", NotificationMessageType.Success); + hostsManager.AddRange(recivedHosts); + } + else + { + ApiManager.SendMessageNewLine($"Invalid response!", NotificationMessageType.Error); + } + } + catch (SocketException ex) + { + ApiManager.SendMessageNewLine($"Invalid host! Error message: {ex.Message}", NotificationMessageType.Error); + } + catch (IOException ex) + { + ApiManager.SendMessageNewLine($"Invalid host! Error message: {ex.Message}", NotificationMessageType.Error); + } + } + else + { + ApiManager.SendMessageNewLine("Invalid IP address!", NotificationMessageType.Error); + } + } +} \ No newline at end of file diff --git a/HyperbolicDownloader/FileProcessing/FileCompressor.cs b/HyperbolicDownloaderApi/FileProcessing/FileCompressor.cs similarity index 96% rename from HyperbolicDownloader/FileProcessing/FileCompressor.cs rename to HyperbolicDownloaderApi/FileProcessing/FileCompressor.cs index 2c92498..e37492e 100644 --- a/HyperbolicDownloader/FileProcessing/FileCompressor.cs +++ b/HyperbolicDownloaderApi/FileProcessing/FileCompressor.cs @@ -1,6 +1,6 @@ using System.IO.Compression; -namespace HyperbolicDownloader.FileProcessing; +namespace HyperbolicDownloaderApi.FileProcessing; internal static class FileCompressor { diff --git a/HyperbolicDownloader/FileProcessing/FileValidator.cs b/HyperbolicDownloaderApi/FileProcessing/FileValidator.cs similarity index 90% rename from HyperbolicDownloader/FileProcessing/FileValidator.cs rename to HyperbolicDownloaderApi/FileProcessing/FileValidator.cs index 0bacae9..392676a 100644 --- a/HyperbolicDownloader/FileProcessing/FileValidator.cs +++ b/HyperbolicDownloaderApi/FileProcessing/FileValidator.cs @@ -1,8 +1,8 @@ using System.Security.Cryptography; -namespace HyperbolicDownloader.FileProcessing; +namespace HyperbolicDownloaderApi.FileProcessing; -internal class FileValidator +internal static class FileValidator { public static async Task CalculateHashAsync(string filePath) { diff --git a/HyperbolicDownloader/FileProcessing/FilesManager.cs b/HyperbolicDownloaderApi/FileProcessing/FilesManager.cs similarity index 89% rename from HyperbolicDownloader/FileProcessing/FilesManager.cs rename to HyperbolicDownloaderApi/FileProcessing/FilesManager.cs index 089de5f..d586503 100644 --- a/HyperbolicDownloader/FileProcessing/FilesManager.cs +++ b/HyperbolicDownloaderApi/FileProcessing/FilesManager.cs @@ -1,8 +1,10 @@ -using System.Text.Json; +using HyperbolicDownloaderApi.Managment; -namespace HyperbolicDownloader.FileProcessing; +using System.Text.Json; -internal class FilesManager +namespace HyperbolicDownloaderApi.FileProcessing; + +public class FilesManager { private readonly List files = new List(); @@ -91,6 +93,6 @@ internal class FilesManager private void SaveFiles() { - File.WriteAllText(Program.FilesInfoPath, JsonSerializer.Serialize(files)); + File.WriteAllText(ApiConfiguration.FilesInfoPath, JsonSerializer.Serialize(files)); } } \ No newline at end of file diff --git a/HyperbolicDownloader/FileProcessing/PrivateHyperFileInfo.cs b/HyperbolicDownloaderApi/FileProcessing/PrivateHyperFileInfo.cs similarity index 70% rename from HyperbolicDownloader/FileProcessing/PrivateHyperFileInfo.cs rename to HyperbolicDownloaderApi/FileProcessing/PrivateHyperFileInfo.cs index b5a7b36..6acf9f5 100644 --- a/HyperbolicDownloader/FileProcessing/PrivateHyperFileInfo.cs +++ b/HyperbolicDownloaderApi/FileProcessing/PrivateHyperFileInfo.cs @@ -1,6 +1,6 @@ -namespace HyperbolicDownloader.FileProcessing; +namespace HyperbolicDownloaderApi.FileProcessing; -internal class PrivateHyperFileInfo +public class PrivateHyperFileInfo { public string Hash { get; set; } public string FilePath { get; set; } diff --git a/HyperbolicDownloader/FileProcessing/PublicHyperFileInfo.cs b/HyperbolicDownloaderApi/FileProcessing/PublicHyperFileInfo.cs similarity index 74% rename from HyperbolicDownloader/FileProcessing/PublicHyperFileInfo.cs rename to HyperbolicDownloaderApi/FileProcessing/PublicHyperFileInfo.cs index 28c0107..330ba46 100644 --- a/HyperbolicDownloader/FileProcessing/PublicHyperFileInfo.cs +++ b/HyperbolicDownloaderApi/FileProcessing/PublicHyperFileInfo.cs @@ -1,8 +1,8 @@ -using HyperbolicDownloader.Networking; +using HyperbolicDownloaderApi.Networking; -namespace HyperbolicDownloader.FileProcessing; +namespace HyperbolicDownloaderApi.FileProcessing; -internal class PublicHyperFileInfo +public class PublicHyperFileInfo { public string Hash { get; set; } = string.Empty; public List Hosts { get; set; } = new(); diff --git a/HyperbolicDownloaderApi/HyperbolicDownloaderApi.csproj b/HyperbolicDownloaderApi/HyperbolicDownloaderApi.csproj new file mode 100644 index 0000000..957ae0e --- /dev/null +++ b/HyperbolicDownloaderApi/HyperbolicDownloaderApi.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/HyperbolicDownloaderApi/Managment/ApiConfiguration.cs b/HyperbolicDownloaderApi/Managment/ApiConfiguration.cs new file mode 100644 index 0000000..3dd3fd7 --- /dev/null +++ b/HyperbolicDownloaderApi/Managment/ApiConfiguration.cs @@ -0,0 +1,13 @@ +using System.Reflection; + +namespace HyperbolicDownloaderApi.Managment; +public static class ApiConfiguration +{ + public const int BroadcastPort = 2155; + public const int PrivatePort = 3055; + public static int PublicPort { get; set; } + public static string BasePath { get; } = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location) ?? string.Empty; + public static string HostsFilePath { get; } = Path.Combine(BasePath, "Hosts.json"); + public static string FilesInfoPath { get; } = Path.Combine(BasePath, "Files.json"); + +} diff --git a/HyperbolicDownloaderApi/Managment/ApiManager.cs b/HyperbolicDownloaderApi/Managment/ApiManager.cs new file mode 100644 index 0000000..41a2884 --- /dev/null +++ b/HyperbolicDownloaderApi/Managment/ApiManager.cs @@ -0,0 +1,213 @@ +using HyperbolicDownloaderApi.FileProcessing; +using HyperbolicDownloaderApi.Networking; + +using Open.Nat; + +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; + +namespace HyperbolicDownloaderApi.Managment; + +public class ApiManager +{ + public static event EventHandler? OnNotificationMessageRecived; + + public static IPAddress? PublicIpAddress => device?.GetExternalIPAsync().GetAwaiter().GetResult(); + public FilesManager FilesManager { get; } = new(); + public HostsManager HostsManager { get; } = new(); + + private static readonly Random random = new(); + private static NatDevice? device; + private static Mapping? portMapping; + private readonly BroadcastClient broadcastClient = new BroadcastClient(); + private readonly NetworkClient networkClient; + + public ApiManager() + { + networkClient = new(FilesManager); + } + + public static void ClosePorts() + { + ApiManager.SendMessageNewLine("Closing ports...", NotificationMessageType.Success); + if (device is not null) + { + try + { + IEnumerable? mappings = device.GetAllMappingsAsync().GetAwaiter().GetResult(); + foreach (Mapping? mapping in mappings) + { + if (mapping.Description.Contains("HyperbolicDowloader") && mapping.PrivateIP.ToString() == portMapping?.PrivateIP.ToString()) + { + device.DeletePortMapAsync(mapping).GetAwaiter().GetResult(); + } + } + } + catch (Exception ex) + { + ApiManager.SendMessageNewLine(ex.ToString(), NotificationMessageType.Error); + } + } + + ApiManager.SendMessageNewLine("Ports closed!", NotificationMessageType.Warning); + Environment.Exit(0); + } + + public static NetworkSocket? GetLocalSocket() + { + int port = ApiConfiguration.PublicPort; + string? ipAddress = null; + + if (device is not null) + { + ipAddress = (device.GetExternalIPAsync()).GetAwaiter().GetResult()?.ToString(); + } + + if (ipAddress is null || ipAddress == "0.0.0.0") + { + ipAddress = NetworkUtilities.GetIP4Adress()?.ToString(); + port = ApiConfiguration.PrivatePort; + } + + if (ipAddress is null) + { + return null; + } + + return new NetworkSocket(ipAddress, port, DateTime.Now); + } + + public static async Task OpenPorts() + { + try + { + ApiConfiguration.PublicPort = random.Next(1000, 6000); + + NatDiscoverer? discoverer = new NatDiscoverer(); + device = await discoverer.DiscoverDeviceAsync(); + + IPAddress? ip = await device.GetExternalIPAsync(); + ApiManager.SendMessageNewLine($"The public IP address is: {ip} ", NotificationMessageType.Success); + + portMapping = new Mapping(Protocol.Tcp, ApiConfiguration.PrivatePort, ApiConfiguration.PublicPort, "HyperbolicDowloader"); + + await device.CreatePortMapAsync(portMapping); + + ApiManager.SendMessageNewLine($"The public port is: {ApiConfiguration.PublicPort}", NotificationMessageType.Success); + return true; + } + catch (NatDeviceNotFoundException) + { + ApiManager.SendMessageNewLine($"Could not find a UPnP or NAT-PMP device!", NotificationMessageType.Error); + return false; + } + catch (MappingException ex) + { + ApiManager.SendMessageNewLine($"An error occurred while mapping the private port ({ApiConfiguration.PrivatePort}) to the public port ({ApiConfiguration.PublicPort})! Error message: {ex.Message}", NotificationMessageType.Error); + return false; + } + } + + public void StartBroadcastListener() + { + broadcastClient.StartListening(ApiConfiguration.BroadcastPort); + broadcastClient.OnBroadcastRecived += BroadcastClient_OnBroadcastRecived; + } + + public void StartTcpListener() + { + try + { + networkClient.ListenTo("GetHostsList", GetHostList); + networkClient.ListenTo>("DiscoverAnswer", DiscoverAnswer); + networkClient.ListenTo("Message", ReciveMessage); + networkClient.ListenTo("HasFile", HasFile); + networkClient.StartListening(ApiConfiguration.PrivatePort); + } + catch (SocketException ex) + { + ApiManager.SendMessageNewLine($"An error occurred while starting the TCP listener! Error message: {ex.Message}", NotificationMessageType.Error); // net stop hns && net start hns + Console.ReadKey(); + } + } + + private async void BroadcastClient_OnBroadcastRecived(object? sender, BroadcastRecivedEventArgs recivedEventArgs) + { + Debug.WriteLine($"Received broadcast \"{recivedEventArgs.Message}\" from {recivedEventArgs.IPEndPoint.Address}"); + List hostsToSend = HostsManager.ToList(); + + NetworkSocket? localSocket = GetLocalSocket(); + + if (localSocket is null) + { + return; + } + + hostsToSend.RemoveAll(x => x.IPAddress == recivedEventArgs.IPEndPoint.Address.ToString()); + hostsToSend.Add(localSocket); + + bool success = int.TryParse(recivedEventArgs.Message, out int remotePort); + + if (success) + { + HostsManager.Add(new NetworkSocket(recivedEventArgs.IPEndPoint.Address.ToString(), remotePort, DateTime.Now)); + try + { + await NetworkClient.SendAsync(recivedEventArgs.IPEndPoint.Address, remotePort, "DiscoverAnswer", hostsToSend); + } + catch (SocketException ex) + { + Debug.WriteLine(ex); + } + } + } + + private void DiscoverAnswer(object? sender, MessageRecivedEventArgs> recivedEventArgs) + { + ApiManager.SendMessageNewLine($"Received answer from {recivedEventArgs.IpAddress}. Returned {recivedEventArgs.Data.Count} host(s).", NotificationMessageType.Raw); + HostsManager.AddRange(recivedEventArgs.Data); + } + + private async void GetHostList(object? sender, MessageRecivedEventArgs recivedEventArgs) + { + List hostsToSend = HostsManager.ToList(); + + NetworkSocket? localSocket = GetLocalSocket(); + + if (localSocket is null) + { + return; + } + + hostsToSend.RemoveAll(x => x.IPAddress == recivedEventArgs.IpAddress.ToString()); + hostsToSend.Add(localSocket); + + if (recivedEventArgs.Data.Port != 0) + { + HostsManager.Add(recivedEventArgs.Data); + } + + await recivedEventArgs.SendResponseAsync(hostsToSend); + } + + private async void HasFile(object? sender, MessageRecivedEventArgs recivedEventArgs) + { + await recivedEventArgs.SendResponseAsync(FilesManager.Contains(recivedEventArgs.Data)); + } + + private void ReciveMessage(object? sender, MessageRecivedEventArgs recivedEventArgs) + { + ApiManager.SendMessageNewLine($"Received \"{recivedEventArgs.Data}\" from {recivedEventArgs.IpAddress}.", NotificationMessageType.Raw); + } + + internal static void SendMessage(string message, NotificationMessageType messageType = NotificationMessageType.Raw) + { + OnNotificationMessageRecived?.Invoke(null, new NotificationMessageEventArgs(messageType, message)); + } + + internal static void SendMessageNewLine(string message, NotificationMessageType messageType = NotificationMessageType.Raw) + { + OnNotificationMessageRecived?.Invoke(null, new NotificationMessageEventArgs(messageType, message + Environment.NewLine)); + } +} \ No newline at end of file diff --git a/HyperbolicDownloaderApi/Managment/NotificationMessageEventArgs.cs b/HyperbolicDownloaderApi/Managment/NotificationMessageEventArgs.cs new file mode 100644 index 0000000..b012309 --- /dev/null +++ b/HyperbolicDownloaderApi/Managment/NotificationMessageEventArgs.cs @@ -0,0 +1,21 @@ +namespace HyperbolicDownloaderApi.Managment; + +public class NotificationMessageEventArgs : EventArgs +{ + public NotificationMessageType NotificationMessageType { get; } + public string? Message { get; } + + public NotificationMessageEventArgs(NotificationMessageType notificationMessageType, string? message) + { + NotificationMessageType = notificationMessageType; + Message = message; + } +} + +public enum NotificationMessageType +{ + Raw, + Success, + Warning, + Error +} \ No newline at end of file diff --git a/HyperbolicDownloader/Networking/BroadcastClient.cs b/HyperbolicDownloaderApi/Networking/BroadcastClient.cs similarity index 84% rename from HyperbolicDownloader/Networking/BroadcastClient.cs rename to HyperbolicDownloaderApi/Networking/BroadcastClient.cs index 4bdd19a..7d666ce 100644 --- a/HyperbolicDownloader/Networking/BroadcastClient.cs +++ b/HyperbolicDownloaderApi/Networking/BroadcastClient.cs @@ -1,11 +1,11 @@ -using Stone_Red_Utilities.ConsoleExtentions; +using HyperbolicDownloaderApi.Managment; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; -namespace HyperbolicDownloader.Networking; +namespace HyperbolicDownloaderApi.Networking; internal class BroadcastClient { @@ -26,7 +26,7 @@ internal class BroadcastClient if (ip4Address is null) { - ConsoleExt.WriteLine("Could not find suitable network adapter!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("Could not find suitable network adapter!", NotificationMessageType.Error); return; } @@ -34,7 +34,7 @@ internal class BroadcastClient if (addressInformation is null) { - ConsoleExt.WriteLine("Could not find suitable network adapter!", ConsoleColor.Red); + ApiManager.SendMessageNewLine("Could not find suitable network adapter!", NotificationMessageType.Error); return; } @@ -48,7 +48,7 @@ internal class BroadcastClient public void StartListening(int port) { - if (IsListening == true) + if (IsListening) { throw new InvalidOperationException("Already listening!"); } diff --git a/HyperbolicDownloader/Networking/BroadcastRecivedEventArgs.cs b/HyperbolicDownloaderApi/Networking/BroadcastRecivedEventArgs.cs similarity index 86% rename from HyperbolicDownloader/Networking/BroadcastRecivedEventArgs.cs rename to HyperbolicDownloaderApi/Networking/BroadcastRecivedEventArgs.cs index 7fa5770..10fa0b1 100644 --- a/HyperbolicDownloader/Networking/BroadcastRecivedEventArgs.cs +++ b/HyperbolicDownloaderApi/Networking/BroadcastRecivedEventArgs.cs @@ -1,6 +1,6 @@ using System.Net; -namespace HyperbolicDownloader.Networking; +namespace HyperbolicDownloaderApi.Networking; internal class BroadcastRecivedEventArgs : EventArgs { diff --git a/HyperbolicDownloader/Networking/DataContainer.cs b/HyperbolicDownloaderApi/Networking/DataContainer.cs similarity index 88% rename from HyperbolicDownloader/Networking/DataContainer.cs rename to HyperbolicDownloaderApi/Networking/DataContainer.cs index dbc98fd..42ae3a3 100644 --- a/HyperbolicDownloader/Networking/DataContainer.cs +++ b/HyperbolicDownloaderApi/Networking/DataContainer.cs @@ -1,4 +1,4 @@ -namespace HyperbolicDownloader +namespace HyperbolicDownloaderApi { internal class DataContainer { diff --git a/HyperbolicDownloader/Networking/HostsManager.cs b/HyperbolicDownloaderApi/Networking/HostsManager.cs similarity index 77% rename from HyperbolicDownloader/Networking/HostsManager.cs rename to HyperbolicDownloaderApi/Networking/HostsManager.cs index 88266f3..60f18c4 100644 --- a/HyperbolicDownloader/Networking/HostsManager.cs +++ b/HyperbolicDownloaderApi/Networking/HostsManager.cs @@ -1,13 +1,12 @@ -using HyperbolicDownloader.Networking; - -using Stone_Red_Utilities.ConsoleExtentions; +using HyperbolicDownloaderApi.Managment; +using HyperbolicDownloaderApi.Networking; using System.Net.Sockets; using System.Text.Json; -namespace HyperbolicDownloader; +namespace HyperbolicDownloaderApi; -internal class HostsManager +public class HostsManager { private List hosts = new List(); public int Count => hosts.Count; @@ -54,7 +53,7 @@ internal class HostsManager int activeHostsCount = 0; foreach (NetworkSocket host in hosts) { - ConsoleExt.Write($"{host.IPAddress}:{host.Port} > ???", ConsoleColor.DarkYellow); + ApiManager.SendMessage($"{host.IPAddress}:{host.Port} > ???", NotificationMessageType.Warning); using TcpClient tcpClient = new TcpClient(); try @@ -63,7 +62,7 @@ internal class HostsManager Console.CursorLeft = 0; if (tcpClient.Connected) { - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Active", ConsoleColor.Green); + ApiManager.SendMessageNewLine($"{host.IPAddress}:{host.Port} > Active", NotificationMessageType.Success); host.LastActive = DateTime.Now; activeHostsCount++; } @@ -73,7 +72,7 @@ internal class HostsManager { hostsToRemove.Add(host); } - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Inactive", ConsoleColor.Red); + ApiManager.SendMessageNewLine($"{host.IPAddress}:{host.Port} > Inactive", NotificationMessageType.Error); } } catch @@ -83,7 +82,7 @@ internal class HostsManager hostsToRemove.Add(host); } Console.CursorLeft = 0; - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Inactive", ConsoleColor.Red); + ApiManager.SendMessageNewLine($"{host.IPAddress}:{host.Port} > Inactive", NotificationMessageType.Error); } } @@ -104,6 +103,6 @@ internal class HostsManager public void SaveHosts() { hosts = hosts.OrderByDescending(h => h.LastActive).ToList(); - File.WriteAllText(Program.HostsFilePath, JsonSerializer.Serialize(hosts)); + File.WriteAllText(ApiConfiguration.HostsFilePath, JsonSerializer.Serialize(hosts)); } } \ No newline at end of file diff --git a/HyperbolicDownloader/Networking/MessageRecivedEventArgs.cs b/HyperbolicDownloaderApi/Networking/MessageRecivedEventArgs.cs similarity index 96% rename from HyperbolicDownloader/Networking/MessageRecivedEventArgs.cs rename to HyperbolicDownloaderApi/Networking/MessageRecivedEventArgs.cs index c7e77c9..217bac8 100644 --- a/HyperbolicDownloader/Networking/MessageRecivedEventArgs.cs +++ b/HyperbolicDownloaderApi/Networking/MessageRecivedEventArgs.cs @@ -3,7 +3,7 @@ using System.Net.Sockets; using System.Text; using System.Text.Json; -namespace HyperbolicDownloader +namespace HyperbolicDownloaderApi { internal class MessageRecivedEventArgs : EventArgs { diff --git a/HyperbolicDownloader/Networking/NetworkClient.cs b/HyperbolicDownloaderApi/Networking/NetworkClient.cs similarity index 98% rename from HyperbolicDownloader/Networking/NetworkClient.cs rename to HyperbolicDownloaderApi/Networking/NetworkClient.cs index 54c7609..95fa50d 100644 --- a/HyperbolicDownloader/Networking/NetworkClient.cs +++ b/HyperbolicDownloaderApi/Networking/NetworkClient.cs @@ -1,4 +1,4 @@ -using HyperbolicDownloader.FileProcessing; +using HyperbolicDownloaderApi.FileProcessing; using System.Diagnostics; using System.Net; @@ -6,7 +6,7 @@ using System.Net.Sockets; using System.Text; using System.Text.Json; -namespace HyperbolicDownloader.Networking +namespace HyperbolicDownloaderApi.Networking { internal class NetworkClient { diff --git a/HyperbolicDownloader/Networking/NetworkSocket.cs b/HyperbolicDownloaderApi/Networking/NetworkSocket.cs similarity index 79% rename from HyperbolicDownloader/Networking/NetworkSocket.cs rename to HyperbolicDownloaderApi/Networking/NetworkSocket.cs index 9886bc4..6b8c702 100644 --- a/HyperbolicDownloader/Networking/NetworkSocket.cs +++ b/HyperbolicDownloaderApi/Networking/NetworkSocket.cs @@ -1,6 +1,6 @@ -namespace HyperbolicDownloader.Networking; +namespace HyperbolicDownloaderApi.Networking; -internal class NetworkSocket +public class NetworkSocket { public string IPAddress { get; set; } public int Port { get; set; } diff --git a/HyperbolicDownloader/Networking/NetworkUtilities.cs b/HyperbolicDownloaderApi/Networking/NetworkUtilities.cs similarity index 95% rename from HyperbolicDownloader/Networking/NetworkUtilities.cs rename to HyperbolicDownloaderApi/Networking/NetworkUtilities.cs index 910353a..a9c95eb 100644 --- a/HyperbolicDownloader/Networking/NetworkUtilities.cs +++ b/HyperbolicDownloaderApi/Networking/NetworkUtilities.cs @@ -2,9 +2,9 @@ using System.Net.NetworkInformation; using System.Net.Sockets; -namespace HyperbolicDownloader.Networking; +namespace HyperbolicDownloaderApi.Networking; -internal static class NetworkUtilities +public static class NetworkUtilities { public static UnicastIPAddressInformation? GetUnicastIPAddressInformation(IPAddress address) {