From ad5bb5b49af49c4668fd30fb2a0e606dcefe4ec0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 19:39:13 -0400 Subject: proxy Pastebin requests through our API instead of third parties, improve error-handling (#358) --- .../Framework/LogParser/GetPasteResponse.cs | 15 +++ .../Framework/LogParser/PastebinClient.cs | 110 +++++++++++++++++++++ .../Framework/LogParser/SavePasteResponse.cs | 15 +++ 3 files changed, 140 insertions(+) create mode 100644 src/SMAPI.Web/Framework/LogParser/GetPasteResponse.cs create mode 100644 src/SMAPI.Web/Framework/LogParser/PastebinClient.cs create mode 100644 src/SMAPI.Web/Framework/LogParser/SavePasteResponse.cs (limited to 'src/SMAPI.Web/Framework/LogParser') diff --git a/src/SMAPI.Web/Framework/LogParser/GetPasteResponse.cs b/src/SMAPI.Web/Framework/LogParser/GetPasteResponse.cs new file mode 100644 index 00000000..4f8794db --- /dev/null +++ b/src/SMAPI.Web/Framework/LogParser/GetPasteResponse.cs @@ -0,0 +1,15 @@ +namespace StardewModdingAPI.Web.Framework.LogParser +{ + /// The response for a get-paste request. + internal class GetPasteResponse + { + /// Whether the log was successfully fetched. + public bool Success { get; set; } + + /// The fetched paste content (if is true). + public string Content { get; set; } + + /// The error message (if saving failed). + public string Error { get; set; } + } +} diff --git a/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs new file mode 100644 index 00000000..8536f249 --- /dev/null +++ b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Pathoschild.Http.Client; + +namespace StardewModdingAPI.Web.Framework.LogParser +{ + /// An API client for Pastebin. + internal class PastebinClient : IDisposable + { + /********* + ** Properties + *********/ + /// The underlying HTTP client. + private readonly IClient Client; + + /// The developer key used to authenticate with the Pastebin API. + private readonly string DevKey; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The base URL for the Pastebin API. + /// The user agent for the API client. + /// The developer key used to authenticate with the Pastebin API. + public PastebinClient(string baseUrl, string userAgent, string devKey) + { + this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent); + this.DevKey = devKey; + } + + /// Fetch a saved paste. + /// The paste ID. + public async Task GetAsync(string id) + { + try + { + // get from API + string content = await this.Client + .GetAsync($"raw/{id}") + .AsString(); + + // handle Pastebin errors + if (string.IsNullOrWhiteSpace(content)) + return new GetPasteResponse { Error = "Received an empty response from Pastebin." }; + if (content.StartsWith(" PostAsync(string content) + { + try + { + // validate + if (string.IsNullOrWhiteSpace(content)) + return new SavePasteResponse { Error = "The log content can't be empty." }; + + // post to API + string response = await this.Client + .PostAsync("api/api_post.php") + .WithBodyContent(new FormUrlEncodedContent(new Dictionary + { + ["api_dev_key"] = "b8219d942109d1e60ebb14fbb45f06f9", + ["api_option"] = "paste", + ["api_paste_private"] = "1", + ["api_paste_code"] = content, + ["api_paste_expire_date"] = "1W" + })) + .AsString(); + + // handle Pastebin errors + if (string.IsNullOrWhiteSpace(response)) + return new SavePasteResponse { Error = "Received an empty response from Pastebin." }; + if (response.StartsWith("Bad API request")) + return new SavePasteResponse { Error = response }; + if (!response.Contains("/")) + return new SavePasteResponse { Error = $"Received an unknown response: {response}" }; + + // return paste ID + string pastebinID = response.Split("/").Last(); + return new SavePasteResponse { Success = true, ID = pastebinID }; + } + catch (Exception ex) + { + return new SavePasteResponse { Success = false, Error = ex.ToString() }; + } + } + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + public void Dispose() + { + this.Client.Dispose(); + } + } +} diff --git a/src/SMAPI.Web/Framework/LogParser/SavePasteResponse.cs b/src/SMAPI.Web/Framework/LogParser/SavePasteResponse.cs new file mode 100644 index 00000000..1c0960a4 --- /dev/null +++ b/src/SMAPI.Web/Framework/LogParser/SavePasteResponse.cs @@ -0,0 +1,15 @@ +namespace StardewModdingAPI.Web.Framework.LogParser +{ + /// The response for a save-log request. + internal class SavePasteResponse + { + /// Whether the log was successfully saved. + public bool Success { get; set; } + + /// The saved paste ID (if is true). + public string ID { get; set; } + + /// The error message (if saving failed). + public string Error { get; set; } + } +} -- cgit