diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2019-11-24 13:49:30 -0500 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2019-11-24 13:49:30 -0500 |
commit | a3f21685049cabf2d824c8060dc0b1de47e9449e (patch) | |
tree | ad9add30e9da2a50e0ea0245f1546b7378f0d282 /src/SMAPI.Web/Controllers/LogParserController.cs | |
parent | 6521df7b131924835eb797251c1e956fae0d6e13 (diff) | |
parent | 277bf082675b98b95bf6184fe3c7a45b969c7ac2 (diff) | |
download | SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.tar.gz SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.tar.bz2 SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.zip |
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI.Web/Controllers/LogParserController.cs')
-rw-r--r-- | src/SMAPI.Web/Controllers/LogParserController.cs | 109 |
1 files changed, 38 insertions, 71 deletions
diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs index 21e4a56f..f7f19cd8 100644 --- a/src/SMAPI.Web/Controllers/LogParserController.cs +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -1,13 +1,12 @@ using System; -using System.IO; -using System.IO.Compression; using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; +using StardewModdingAPI.Toolkit.Utilities; using StardewModdingAPI.Web.Framework; using StardewModdingAPI.Web.Framework.Clients.Pastebin; +using StardewModdingAPI.Web.Framework.Compression; using StardewModdingAPI.Web.Framework.ConfigModels; using StardewModdingAPI.Web.Framework.LogParsing; using StardewModdingAPI.Web.Framework.LogParsing.Models; @@ -27,9 +26,8 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>The underlying Pastebin client.</summary> private readonly IPastebinClient Pastebin; - /// <summary>The first bytes in a valid zip file.</summary> - /// <remarks>See <a href="https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers"/>.</remarks> - private const uint GzipLeadBytes = 0x8b1f; + /// <summary>The underlying text compression helper.</summary> + private readonly IGzipHelper GzipHelper; /********* @@ -41,10 +39,12 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>Construct an instance.</summary> /// <param name="siteConfig">The context config settings.</param> /// <param name="pastebin">The Pastebin API client.</param> - public LogParserController(IOptions<SiteConfig> siteConfig, IPastebinClient pastebin) + /// <param name="gzipHelper">The underlying text compression helper.</param> + public LogParserController(IOptions<SiteConfig> siteConfig, IPastebinClient pastebin, IGzipHelper gzipHelper) { this.Config = siteConfig.Value; this.Pastebin = pastebin; + this.GzipHelper = gzipHelper; } /*** @@ -60,14 +60,14 @@ namespace StardewModdingAPI.Web.Controllers { // fresh page if (string.IsNullOrWhiteSpace(id)) - return this.View("Index", new LogParserModel(this.Config.LogParserUrl, id)); + return this.View("Index", this.GetModel(id)); // log page PasteInfo paste = await this.GetAsync(id); ParsedLog log = paste.Success ? new LogParser().Parse(paste.Content) : new ParsedLog { IsValid = false, Error = "Pastebin error: " + paste.Error }; - return this.View("Index", new LogParserModel(this.Config.LogParserUrl, id, log, raw)); + return this.View("Index", this.GetModel(id).SetResult(log, raw)); } /*** @@ -81,15 +81,15 @@ namespace StardewModdingAPI.Web.Controllers // get raw log text string input = this.Request.Form["input"].FirstOrDefault(); if (string.IsNullOrWhiteSpace(input)) - return this.View("Index", new LogParserModel(this.Config.LogParserUrl, null) { UploadError = "The log file seems to be empty." }); + return this.View("Index", this.GetModel(null, uploadError: "The log file seems to be empty.")); // upload log - input = this.CompressString(input); - SavePasteResult result = await this.Pastebin.PostAsync(input); + input = this.GzipHelper.CompressString(input); + SavePasteResult result = await this.Pastebin.PostAsync($"SMAPI log {DateTime.UtcNow:s}", input); // handle errors if (!result.Success) - return this.View("Index", new LogParserModel(this.Config.LogParserUrl, result.ID) { UploadError = $"Pastebin error: {result.Error ?? "unknown error"}" }); + return this.View("Index", this.GetModel(result.ID, uploadError: $"Pastebin error: {result.Error ?? "unknown error"}")); // redirect to view UriBuilder uri = new UriBuilder(new Uri(this.Config.LogParserUrl)); @@ -106,74 +106,41 @@ namespace StardewModdingAPI.Web.Controllers private async Task<PasteInfo> GetAsync(string id) { PasteInfo response = await this.Pastebin.GetAsync(id); - response.Content = this.DecompressString(response.Content); + response.Content = this.GzipHelper.DecompressString(response.Content); return response; } - /// <summary>Compress a string.</summary> - /// <param name="text">The text to compress.</param> - /// <remarks>Derived from <a href="https://stackoverflow.com/a/17993002/262123"/>.</remarks> - private string CompressString(string text) + /// <summary>Build a log parser model.</summary> + /// <param name="pasteID">The paste ID.</param> + /// <param name="uploadError">An error which occurred while uploading the log to Pastebin.</param> + private LogParserModel GetModel(string pasteID, string uploadError = null) { - // get raw bytes - byte[] buffer = Encoding.UTF8.GetBytes(text); - - // compressed - byte[] compressedData; - using (MemoryStream stream = new MemoryStream()) - { - using (GZipStream zipStream = new GZipStream(stream, CompressionLevel.Optimal, leaveOpen: true)) - zipStream.Write(buffer, 0, buffer.Length); - - stream.Position = 0; - compressedData = new byte[stream.Length]; - stream.Read(compressedData, 0, compressedData.Length); - } - - // prefix length - byte[] zipBuffer = new byte[compressedData.Length + 4]; - Buffer.BlockCopy(compressedData, 0, zipBuffer, 4, compressedData.Length); - Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, zipBuffer, 0, 4); - - // return string representation - return Convert.ToBase64String(zipBuffer); + string sectionUrl = this.Config.LogParserUrl; + Platform? platform = this.DetectClientPlatform(); + return new LogParserModel(sectionUrl, pasteID, platform) { UploadError = uploadError }; } - /// <summary>Decompress a string.</summary> - /// <param name="rawText">The compressed text.</param> - /// <remarks>Derived from <a href="https://stackoverflow.com/a/17993002/262123"/>.</remarks> - private string DecompressString(string rawText) + /// <summary>Detect the viewer's OS.</summary> + /// <returns>Returns the viewer OS if known, else null.</returns> + private Platform? DetectClientPlatform() { - // get raw bytes - byte[] zipBuffer; - try + string userAgent = this.Request.Headers["User-Agent"]; + switch (userAgent) { - zipBuffer = Convert.FromBase64String(rawText); - } - catch - { - return rawText; // not valid base64, wasn't compressed by the log parser - } + case string ua when ua.Contains("Windows"): + return Platform.Windows; - // skip if not gzip - if (BitConverter.ToUInt16(zipBuffer, 4) != LogParserController.GzipLeadBytes) - return rawText; + case string ua when ua.Contains("Android"): // check for Android before Linux because Android user agents also contain Linux + return Platform.Android; - // decompress - using (MemoryStream memoryStream = new MemoryStream()) - { - // read length prefix - int dataLength = BitConverter.ToInt32(zipBuffer, 0); - memoryStream.Write(zipBuffer, 4, zipBuffer.Length - 4); - - // read data - byte[] buffer = new byte[dataLength]; - memoryStream.Position = 0; - using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) - gZipStream.Read(buffer, 0, buffer.Length); - - // return original string - return Encoding.UTF8.GetString(buffer); + case string ua when ua.Contains("Linux"): + return Platform.Linux; + + case string ua when ua.Contains("Mac"): + return Platform.Mac; + + default: + return null; } } } |