diff --git a/ArchiSteamFarm/ArchiSteamFarm.csproj b/ArchiSteamFarm/ArchiSteamFarm.csproj index ac6e2245d..c15466788 100644 --- a/ArchiSteamFarm/ArchiSteamFarm.csproj +++ b/ArchiSteamFarm/ArchiSteamFarm.csproj @@ -87,6 +87,9 @@ PreserveNewest + + PreserveNewest + diff --git a/ArchiSteamFarm/IPC.cs b/ArchiSteamFarm/IPC.cs index c445b61f3..35650a9c4 100644 --- a/ArchiSteamFarm/IPC.cs +++ b/ArchiSteamFarm/IPC.cs @@ -36,6 +36,15 @@ namespace ArchiSteamFarm { internal static class IPC { internal static bool IsRunning => IsHandlingRequests || IsListening; + private static readonly Dictionary MimeTypes = new Dictionary(6) { + { ".css", "text/css" }, + { ".js", "application/javascript" }, + { ".html", "text/html" }, + { ".ico", "image/x-icon" }, + { ".jpg", "image/jpeg" }, + { ".png", "image/png" } + }; + private static bool IsListening { get { try { @@ -468,26 +477,44 @@ namespace ArchiSteamFarm { return false; } - if (request.Url.Segments.Length < 2) { - return await HandleMainPage(request, response).ConfigureAwait(false); + if ((request.Url.Segments.Length >= 2) && request.Url.Segments[1].Equals("Api/")) { + return await HandleApi(request, response, request.Url.Segments, 2).ConfigureAwait(false); } - switch (request.Url.Segments[1]) { - case "Api/": - return await HandleApi(request, response, request.Url.Segments, 2).ConfigureAwait(false); - default: - return false; - } + return await HandleFile(request, response, request.Url.AbsolutePath).ConfigureAwait(false); } - private static async Task HandleMainPage(HttpListenerRequest request, HttpListenerResponse response) { - if ((request == null) || (response == null)) { - ASF.ArchiLogger.LogNullError(nameof(request) + " || " + nameof(response)); + private static async Task HandleFile(HttpListenerRequest request, HttpListenerResponse response, string absolutePath) { + if ((request == null) || (response == null) || string.IsNullOrEmpty(absolutePath)) { + ASF.ArchiLogger.LogNullError(nameof(request) + " || " + nameof(response) + " || " + nameof(absolutePath)); return false; } - // In the future we'll probably have some friendly admin panel here, for now this is 501 - await ResponseStatusCode(request, response, HttpStatusCode.NotImplemented).ConfigureAwait(false); + switch (request.HttpMethod) { + case HttpMethods.Get: + return await HandleFileGet(request, response, absolutePath).ConfigureAwait(false); + default: + await ResponseStatusCode(request, response, HttpStatusCode.MethodNotAllowed).ConfigureAwait(false); + return true; + } + } + + private static async Task HandleFileGet(HttpListenerRequest request, HttpListenerResponse response, string absolutePath) { + if ((request == null) || (response == null) || string.IsNullOrEmpty(absolutePath)) { + ASF.ArchiLogger.LogNullError(nameof(request) + " || " + nameof(response) + " || " + nameof(absolutePath)); + return false; + } + + string filePath = SharedInfo.WebsiteDirectory + Path.DirectorySeparatorChar + absolutePath.Replace("/", Path.DirectorySeparatorChar.ToString()); + if (Directory.Exists(filePath)) { + filePath = Path.Combine(filePath, "index.html"); + } + + if (!File.Exists(filePath)) { + return false; + } + + await ResponseFile(request, response, filePath).ConfigureAwait(false); return true; } @@ -599,6 +626,27 @@ namespace ArchiSteamFarm { } } + private static async Task ResponseFile(HttpListenerRequest request, HttpListenerResponse response, string filePath) { + if ((request == null) || (response == null) || string.IsNullOrEmpty(filePath)) { + ASF.ArchiLogger.LogNullError(nameof(request) + " || " + nameof(response) + " || " + nameof(filePath)); + return; + } + + try { + response.ContentType = MimeTypes.TryGetValue(Path.GetExtension(filePath), out string mimeType) ? mimeType : "application/octet-stream"; + + byte[] content = await File.ReadAllBytesAsync(filePath).ConfigureAwait(false); + await ResponseBase(request, response, content).ConfigureAwait(false); + } catch (FileNotFoundException) { + await ResponseStatusCode(request, response, HttpStatusCode.NotFound).ConfigureAwait(false); + } catch (ObjectDisposedException) { + // Ignored, request is no longer valid + } catch (Exception e) { + ASF.ArchiLogger.LogGenericException(e); + await ResponseStatusCode(request, response, HttpStatusCode.ServiceUnavailable).ConfigureAwait(false); + } + } + private static async Task ResponseJson(HttpListenerRequest request, HttpListenerResponse response, string json, HttpStatusCode statusCode = HttpStatusCode.OK) { if ((request == null) || (response == null) || string.IsNullOrEmpty(json)) { ASF.ArchiLogger.LogNullError(nameof(request) + " || " + nameof(response) + " || " + nameof(json)); diff --git a/ArchiSteamFarm/SharedInfo.cs b/ArchiSteamFarm/SharedInfo.cs index c3a1853bd..088a5efd4 100644 --- a/ArchiSteamFarm/SharedInfo.cs +++ b/ArchiSteamFarm/SharedInfo.cs @@ -52,6 +52,7 @@ namespace ArchiSteamFarm { internal const string LogFile = "log.txt"; internal const string StatisticsServer = "asf.justarchi.net"; internal const string UpdateDirectory = "_old"; + internal const string WebsiteDirectory = "www"; private const string SourceVariant = "source";