mirror of
https://github.com/openbullet/openbullet.git
synced 2023-10-21 07:33:42 -05:00
Request refactor.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using RuriLib;
|
||||
using RuriLib.Functions.Files;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -33,11 +34,11 @@ namespace OpenBullet
|
||||
{
|
||||
// Check if name is ok
|
||||
if (nameTextbox.Text.Trim() == "") { MessageBox.Show("The name cannot be blank"); return; }
|
||||
else if (nameTextbox.Text != BlockBase.MakeValidFileName(nameTextbox.Text)) { MessageBox.Show("The name contains invalid characters"); return; }
|
||||
else if (nameTextbox.Text != Files.MakeValidFileName(nameTextbox.Text)) { MessageBox.Show("The name contains invalid characters"); return; }
|
||||
|
||||
// Check if category is ok
|
||||
if (categoryCombobox.Text.Trim() == "") categoryCombobox.Text = "Default";
|
||||
else if (categoryCombobox.Text != BlockBase.MakeValidFileName(categoryCombobox.Text)) { MessageBox.Show("The category contains invalid characters"); return; }
|
||||
else if (categoryCombobox.Text != Files.MakeValidFileName(categoryCombobox.Text)) { MessageBox.Show("The category contains invalid characters"); return; }
|
||||
|
||||
((ConfigManager)Caller).CreateConfig(nameTextbox.Text, categoryCombobox.Text, authorTextbox.Text);
|
||||
}
|
||||
|
||||
@@ -361,86 +361,7 @@ namespace RuriLib
|
||||
}
|
||||
|
||||
#region File Utilities
|
||||
/// <summary>
|
||||
/// Saves a Selenium screenshot to a file with automatically generated name.
|
||||
/// </summary>
|
||||
/// <param name="screenshot">The Selenium screenshot</param>
|
||||
/// <param name="data">The BotData used for path creation</param>
|
||||
public static void SaveScreenshot(OpenQA.Selenium.Screenshot screenshot, BotData data)
|
||||
{
|
||||
var path = MakeScreenshotPath(data);
|
||||
data.Screenshots.Add(path);
|
||||
screenshot.SaveAsFile(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a screenshot to a file with automatically generated name.
|
||||
/// </summary>
|
||||
/// <param name="screenshot">The bitmap image</param>
|
||||
/// <param name="data">The BotData used for path creation</param>
|
||||
public static void SaveScreenshot(Bitmap screenshot, BotData data)
|
||||
{
|
||||
var path = MakeScreenshotPath(data);
|
||||
data.Screenshots.Add(path);
|
||||
screenshot.Save(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a stream to a file with automatically generated name.
|
||||
/// </summary>
|
||||
/// <param name="stream">The input stream</param>
|
||||
/// <param name="data">The BotData used for path creation</param>
|
||||
public static void SaveScreenshot(MemoryStream stream, BotData data)
|
||||
{
|
||||
var path = MakeScreenshotPath(data);
|
||||
using (var fileStream = File.Create(path)) { stream.CopyTo(fileStream); }
|
||||
data.Screenshots.Add(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the path for the screenshot file.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData for path creation</param>
|
||||
/// <returns>The path of the file to save the screenshot to</returns>
|
||||
private static string MakeScreenshotPath(BotData data)
|
||||
{
|
||||
var folderName = MakeValidFileName(data.ConfigSettings.Name);
|
||||
var originalFilename = MakeValidFileName(data.Data.Data);
|
||||
// Check if you have to make the folder
|
||||
if (!Directory.Exists($"Screenshots\\{folderName}")) Directory.CreateDirectory($"Screenshots\\{folderName}");
|
||||
|
||||
// Save the file inside the folder
|
||||
var filename = GetFirstAvailableFileName($"Screenshots\\{folderName}\\", originalFilename, "bmp");
|
||||
return $"Screenshots\\{folderName}\\{filename}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first available name in the given folder by incrementing a number at the end of the filename.
|
||||
/// </summary>
|
||||
/// <param name="basePath">The path to the folder</param>
|
||||
/// <param name="fileName">The name of the file without numbers at the end</param>
|
||||
/// <param name="extension">The extension of the file</param>
|
||||
/// <returns>The first available filename (including extension)</returns>
|
||||
public static string GetFirstAvailableFileName(string basePath, string fileName, string extension)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; File.Exists(basePath + fileName + i + "." + extension); i++) { }
|
||||
return fileName + i + "." + extension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixes the filename to be compatible with the filesystem indicization.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file</param>
|
||||
/// <param name="underscore">Whether to replace the unallowed characters with an underscore instead of removing them</param>
|
||||
/// <returns>The valid filename ready to be saved to disk</returns>
|
||||
public static string MakeValidFileName(string name, bool underscore = true)
|
||||
{
|
||||
string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
|
||||
string invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars);
|
||||
|
||||
return Regex.Replace(name, invalidRegStr, underscore ? "_" : "");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Truncates a string to fit a given size and adds ' [...]' (5 characters) at the end to display that the string would be longer.
|
||||
|
||||
+50
-290
@@ -10,6 +10,9 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
using RuriLib.Functions.Requests;
|
||||
using RuriLib.Functions.Files;
|
||||
using MultipartContent = RuriLib.Functions.Requests.MultipartContent;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
@@ -287,7 +290,7 @@ namespace RuriLib
|
||||
break;
|
||||
|
||||
case RequestType.Standard:
|
||||
if (CanContainBody(method))
|
||||
if (Request.CanContainBody(method))
|
||||
{
|
||||
writer
|
||||
.Token("CONTENT")
|
||||
@@ -358,289 +361,86 @@ namespace RuriLib
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
#region Request
|
||||
// Set base URL
|
||||
var localUrl = ReplaceValues(url, data);
|
||||
var cType = ReplaceValues(contentType, data);
|
||||
var oldJar = data.Cookies;
|
||||
// Setup
|
||||
var request = new Request();
|
||||
request.Setup(data.GlobalSettings, AutoRedirect, data.ConfigSettings.MaxRedirects, AcceptEncoding);
|
||||
|
||||
// Create request
|
||||
HttpRequest request = new HttpRequest();
|
||||
|
||||
// Setup options
|
||||
var timeout = data.GlobalSettings.General.RequestTimeout * 1000;
|
||||
request.IgnoreProtocolErrors = true;
|
||||
request.AllowAutoRedirect = autoRedirect;
|
||||
request.EnableEncodingContent = acceptEncoding;
|
||||
request.ReadWriteTimeout = timeout;
|
||||
request.ConnectTimeout = timeout;
|
||||
request.KeepAlive = true;
|
||||
request.MaximumAutomaticRedirections = data.ConfigSettings.MaxRedirects;
|
||||
|
||||
// Check if it has GET parameters
|
||||
if (ParseQuery && localUrl.Contains('?') && localUrl.Contains('='))
|
||||
{
|
||||
// Remove the query from the base URL
|
||||
localUrl = ReplaceValues(url.Split('?')[0], data);
|
||||
data.Log(new LogEntry($"Calling Base URL: {localUrl}", Colors.MediumTurquoise));
|
||||
|
||||
// Parse the GET parameters
|
||||
var getParams = ReplaceValues(url.Split('?')[1], data);
|
||||
var paramList = getParams.Split('&');
|
||||
|
||||
// Build the query, first replace variables in them and encode the parameters
|
||||
foreach (var par in paramList)
|
||||
{
|
||||
var split = par.Split('=');
|
||||
|
||||
// Encode them if needed
|
||||
if (split[0].Contains('%')) split[0] = Uri.EscapeDataString(split[0]);
|
||||
if (split[1].Contains('%')) split[1] = Uri.EscapeDataString(split[1]);
|
||||
|
||||
// Add them to the query
|
||||
request.AddUrlParam(split[0], split[1]);
|
||||
|
||||
data.Log(new LogEntry($"Added Query Parameter: {split[0]} = {split[1]}", Colors.MediumTurquoise));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Log(new LogEntry($"Calling URL: {localUrl}", Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
// Set up the Content and Content-Type
|
||||
HttpContent content = null;
|
||||
switch (requestType)
|
||||
var localUrl = ReplaceValues(Url, data);
|
||||
data.Log(new LogEntry($"Calling URL: {localUrl}", Colors.MediumTurquoise));
|
||||
|
||||
// Set content
|
||||
switch (RequestType)
|
||||
{
|
||||
case RequestType.Standard:
|
||||
var pData = ReplaceValues(Regex.Replace(postData, @"(?<!\\)\\n", Environment.NewLine).Replace(@"\\n", @"\n"), data);
|
||||
request.SetStandardContent(ReplaceValues(PostData, data), ReplaceValues(ContentType, data), Method, EncodeContent, data.LogBuffer);
|
||||
break;
|
||||
|
||||
if (CanContainBody(method))
|
||||
{
|
||||
if (encodeContent)
|
||||
{
|
||||
// Very dirty but it works
|
||||
var nonce = data.Random.Next(1000000, 9999999);
|
||||
pData = pData.Replace("&", $"{nonce}&{nonce}").Replace("=", $"{nonce}={nonce}");
|
||||
pData = string.Join("", BlockFunction.SplitInChunks(pData, 2080)
|
||||
.Select(s => Uri.EscapeDataString(s)))
|
||||
.Replace($"{nonce}%26{nonce}", "&").Replace($"{nonce}%3D{nonce}", "=");
|
||||
}
|
||||
|
||||
content = new StringContent(pData);
|
||||
content.ContentType = cType;
|
||||
data.Log(new LogEntry($"Post Data: {pData}", Colors.MediumTurquoise));
|
||||
}
|
||||
case RequestType.BasicAuth:
|
||||
request.SetBasicAuth(ReplaceValues(AuthUser, data), ReplaceValues(AuthPass, data));
|
||||
break;
|
||||
|
||||
case RequestType.Multipart:
|
||||
var bdry = multipartBoundary != "" ? ReplaceValues(multipartBoundary, data) : GenerateMultipartBoundary();
|
||||
content = new Extreme.Net.MultipartContent(bdry);
|
||||
var mContent = content as Extreme.Net.MultipartContent;
|
||||
data.Log(new LogEntry($"Content-Type: multipart/form-data; boundary={bdry}", Colors.MediumTurquoise));
|
||||
data.Log(new LogEntry("Multipart Data:", Colors.MediumTurquoise));
|
||||
data.Log(new LogEntry(bdry, Colors.MediumTurquoise));
|
||||
foreach (var c in MultipartContents)
|
||||
{
|
||||
var rValue = ReplaceValues(c.Value, data);
|
||||
var rName = ReplaceValues(c.Name, data);
|
||||
var rContentType = ReplaceValues(c.ContentType, data);
|
||||
|
||||
if (c.Type == MultipartContentType.String)
|
||||
var contents = MultipartContents.Select(m =>
|
||||
new MultipartContent()
|
||||
{
|
||||
mContent.Add(new StringContent(rValue), rName);
|
||||
data.Log(new LogEntry($"Content-Disposition: form-data; name=\"{rName}\"{Environment.NewLine}{Environment.NewLine}{rValue}", Colors.MediumTurquoise));
|
||||
}
|
||||
else if (c.Type == MultipartContentType.File)
|
||||
{
|
||||
mContent.Add(new FileContent(rValue), rName, rValue, rContentType);
|
||||
data.Log(new LogEntry($"Content-Disposition: form-data; name=\"{rName}\"; filename=\"{rValue}\"{Environment.NewLine}Content-Type: {rContentType}{Environment.NewLine}{Environment.NewLine}[FILE CONTENT OMITTED]", Colors.MediumTurquoise));
|
||||
}
|
||||
data.Log(new LogEntry(bdry, Colors.MediumTurquoise));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Name = ReplaceValues(m.Name, data),
|
||||
Value = ReplaceValues(m.Value, data),
|
||||
ContentType = ReplaceValues(m.Value, data),
|
||||
Type = m.Type
|
||||
});
|
||||
request.SetMultipartContent(contents, ReplaceValues(MultipartBoundary, data), data.LogBuffer);
|
||||
break;
|
||||
}
|
||||
|
||||
// Set proxy
|
||||
if (data.UseProxies)
|
||||
{
|
||||
request.Proxy = data.Proxy.GetClient();
|
||||
|
||||
try
|
||||
{
|
||||
request.Proxy.ReadWriteTimeout = timeout;
|
||||
request.Proxy.ConnectTimeout = timeout;
|
||||
request.Proxy.Username = data.Proxy.Username;
|
||||
request.Proxy.Password = data.Proxy.Password;
|
||||
}
|
||||
catch { }
|
||||
request.SetProxy(data.Proxy);
|
||||
}
|
||||
|
||||
// Set headers
|
||||
data.Log(new LogEntry("Sent Headers:", Colors.DarkTurquoise));
|
||||
// var fixedNames = Enum.GetNames(typeof(HttpHeader)).Select(n => n.ToLower());
|
||||
foreach (var header in CustomHeaders)
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = ReplaceValues(header.Key, data);
|
||||
var replacedKey = key.Replace("-", "").ToLower(); // Used to compare with the HttpHeader enum
|
||||
var val = ReplaceValues(header.Value, data);
|
||||
var headers = CustomHeaders.Select( h =>
|
||||
new KeyValuePair<string, string> (ReplaceValues(h.Key, data), ReplaceValues(h.Value, data))
|
||||
).ToDictionary(h => h.Key, h => h.Value);
|
||||
request.SetHeaders(CustomHeaders, AcceptEncoding, data.LogBuffer);
|
||||
|
||||
if (replacedKey == "contenttype" && content != null) { continue; } // Disregard additional Content-Type headers
|
||||
if (replacedKey == "acceptencoding" && acceptEncoding) { continue; } // Disregard additional Accept-Encoding headers
|
||||
// else if (fixedNames.Contains(replacedKey)) request.AddHeader((HttpHeader)Enum.Parse(typeof(HttpHeader), replacedKey, true), val);
|
||||
else request.AddHeader(key, val);
|
||||
// Set cookies
|
||||
data.Log(new LogEntry("Sent Cookies:", Colors.MediumTurquoise));
|
||||
|
||||
data.Log(new LogEntry(key + ": " + val, Colors.MediumTurquoise));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Add the authorization header on a Basic Auth request
|
||||
if (requestType == RequestType.BasicAuth)
|
||||
{
|
||||
var usr = ReplaceValues(authUser, data);
|
||||
var pwd = ReplaceValues(authPass, data);
|
||||
var auth = "Basic " + (usr + ":" + pwd).ToBase64();
|
||||
request.AddHeader("Authorization", auth);
|
||||
data.Log(new LogEntry($"Authorization: {auth}", Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
// Add the content-type header
|
||||
if (CanContainBody(method) && content != null && requestType == RequestType.Standard)
|
||||
{
|
||||
data.Log(new LogEntry($"Content-Type: {cType}", Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
// Add new user-defined custom cookies to the bot's cookie jar
|
||||
request.Cookies = new CookieDictionary();
|
||||
foreach (var cookie in CustomCookies)
|
||||
foreach (var cookie in CustomCookies) // Add new user-defined custom cookies to the bot's cookie jar
|
||||
data.Cookies[ReplaceValues(cookie.Key, data)] = ReplaceValues(cookie.Value, data);
|
||||
|
||||
// Set cookies from the bot's cookie jar to the request's CookieDictionary
|
||||
data.Log(new LogEntry("Sent Cookies:", Colors.MediumTurquoise));
|
||||
foreach (var cookie in data.Cookies)
|
||||
{
|
||||
request.Cookies.Add(cookie.Key, cookie.Value);
|
||||
data.Log(new LogEntry($"{cookie.Key}: {cookie.Value}", Colors.MediumTurquoise));
|
||||
}
|
||||
request.SetCookies(data.Cookies, data.LogBuffer);
|
||||
|
||||
// End the request part
|
||||
data.LogNewLine();
|
||||
#endregion
|
||||
|
||||
#region Response
|
||||
// Create the response
|
||||
HttpResponse response = null;
|
||||
|
||||
try
|
||||
// Perform the request
|
||||
(data.Address, data.ResponseCode, data.ResponseHeaders, data.Cookies) = request.Perform(localUrl, Method, data.ConfigSettings.IgnoreResponseErrors, data.LogBuffer);
|
||||
|
||||
// Save the response content
|
||||
switch (ResponseType)
|
||||
{
|
||||
// Get response
|
||||
response = request.Raw(method, localUrl, content);
|
||||
var responseString = "";
|
||||
case ResponseType.String:
|
||||
data.ResponseSource = request.SaveString(ReadResponseSource, data.ResponseHeaders, data.LogBuffer);
|
||||
break;
|
||||
|
||||
// Get address
|
||||
data.Address = response.Address.ToString();
|
||||
data.Log(new LogEntry("Address: " + data.Address, Colors.Cyan));
|
||||
|
||||
// Get code
|
||||
data.ResponseCode = ((int)response.StatusCode).ToString();
|
||||
data.Log(new LogEntry($"Response code: {data.ResponseCode} ({response.StatusCode})", Colors.Cyan));
|
||||
|
||||
// Get headers
|
||||
data.Log(new LogEntry("Received headers:", Colors.DeepPink));
|
||||
var headerList = new List<KeyValuePair<string, string>>();
|
||||
var receivedHeaders = response.EnumerateHeaders();
|
||||
data.ResponseHeaders.Clear();
|
||||
while (receivedHeaders.MoveNext())
|
||||
{
|
||||
var header = receivedHeaders.Current;
|
||||
data.ResponseHeaders.Add(header.Key, header.Value);
|
||||
data.Log(new LogEntry($"{header.Key}: {header.Value}", Colors.LightPink));
|
||||
}
|
||||
if (!response.ContainsHeader(HttpHeader.ContentLength) && ResponseType != ResponseType.File)
|
||||
{
|
||||
responseString = response.ToString(); // Read the stream
|
||||
|
||||
if (data.ResponseHeaders.ContainsKey("Content-Encoding") && data.ResponseHeaders["Content-Encoding"].Contains("gzip"))
|
||||
case ResponseType.File:
|
||||
if (SaveAsScreenshot)
|
||||
{
|
||||
data.ResponseHeaders["Content-Length"] = GZip.Zip(responseString).Length.ToString();
|
||||
Files.SaveScreenshot(request.GetResponseStream(), data); // Read the stream
|
||||
data.Log(new LogEntry("File saved as screenshot", Colors.Green));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ResponseHeaders["Content-Length"] = responseString.Length.ToString();
|
||||
request.SaveFile(ReplaceValues(DownloadPath, data), data.LogBuffer);
|
||||
}
|
||||
break;
|
||||
|
||||
data.Log(new LogEntry($"Content-Length: {data.ResponseHeaders["Content-Length"]}", Colors.LightPink));
|
||||
}
|
||||
|
||||
// Get cookies
|
||||
data.Log(new LogEntry("Received cookies:", Colors.Goldenrod));
|
||||
data.Cookies = response.Cookies;
|
||||
foreach (var cookie in response.Cookies)
|
||||
{
|
||||
// If the cookie was already present before, don't log it
|
||||
if (oldJar.ContainsKey(cookie.Key) && oldJar[cookie.Key] == cookie.Value) continue;
|
||||
|
||||
data.Log(new LogEntry($"{cookie.Key}: {cookie.Value}", Colors.LightGoldenrodYellow));
|
||||
}
|
||||
|
||||
// Save the response content
|
||||
switch (responseType)
|
||||
{
|
||||
case ResponseType.String:
|
||||
data.Log(new LogEntry("Response Source:", Colors.Green));
|
||||
if (readResponseSource)
|
||||
{
|
||||
if (responseString == "") responseString = response.ToString(); // Read the stream if you didn't already read it
|
||||
data.ResponseSource = responseString;
|
||||
data.Log(new LogEntry(data.ResponseSource, Colors.GreenYellow));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ResponseSource = "";
|
||||
data.Log(new LogEntry("[SKIPPED]", Colors.GreenYellow));
|
||||
}
|
||||
break;
|
||||
|
||||
case ResponseType.File:
|
||||
if (SaveAsScreenshot)
|
||||
{
|
||||
SaveScreenshot(response.ToMemoryStream(), data); // Read the stream
|
||||
data.Log(new LogEntry("File saved as screenshot", Colors.Green));
|
||||
}
|
||||
else
|
||||
{
|
||||
var filePath = ReplaceValues(downloadPath, data);
|
||||
var dirName = Path.GetDirectoryName(filePath);
|
||||
if (dirName != "") dirName += Path.DirectorySeparatorChar.ToString();
|
||||
var fileName = Path.GetFileNameWithoutExtension(filePath);
|
||||
var fileExtension = Path.GetExtension(filePath);
|
||||
var sanitizedPath = $"{dirName}{MakeValidFileName(fileName)}{fileExtension}";
|
||||
using (var stream = File.Create(sanitizedPath)) { response.ToMemoryStream().CopyTo(stream); } // Read the stream
|
||||
data.Log(new LogEntry("File saved as " + sanitizedPath, Colors.Green));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
data.Log(new LogEntry(ex.Message, Colors.White));
|
||||
if (ex.GetType() == typeof(HttpException))
|
||||
{
|
||||
data.ResponseCode = ((HttpException)ex).HttpStatusCode.ToString();
|
||||
data.Log(new LogEntry("Status code: " + data.ResponseCode, Colors.Cyan));
|
||||
}
|
||||
|
||||
if (!data.ConfigSettings.IgnoreResponseErrors) throw;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Custom Cookies, Headers and Multipart Contents
|
||||
@@ -746,45 +546,5 @@ namespace RuriLib
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random string to be used for boundary.
|
||||
/// </summary>
|
||||
/// <returns>The random 16-character string</returns>
|
||||
internal static string GenerateMultipartBoundary()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Random random = new Random();
|
||||
char ch;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
|
||||
builder.Append(ch);
|
||||
}
|
||||
return $"------WebKitFormBoundary{builder.ToString().ToLower()}";
|
||||
}
|
||||
|
||||
private static bool CanContainBody(HttpMethod method)
|
||||
{
|
||||
return method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.DELETE;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Multipart Content
|
||||
/// </summary>
|
||||
public class MultipartContent
|
||||
{
|
||||
/// <summary>The type of multipart content.</summary>
|
||||
public MultipartContentType Type { get; set; } = MultipartContentType.String;
|
||||
|
||||
/// <summary>The name of the multipart content.</summary>
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
/// <summary>The value of the multipart content (a string value or a file path).</summary>
|
||||
public string Value { get; set; } = "";
|
||||
|
||||
/// <summary>The Content-Type of the file content.</summary>
|
||||
public string ContentType { get; set; } = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using OpenQA.Selenium.Chrome;
|
||||
using OpenQA.Selenium.Firefox;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
using RuriLib.Functions.Files;
|
||||
using RuriLib.LS;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
@@ -217,7 +218,7 @@ namespace RuriLib
|
||||
|
||||
case BrowserAction.Screenshot:
|
||||
var image = data.Driver.GetScreenshot();
|
||||
SaveScreenshot(image, data);
|
||||
Files.SaveScreenshot(image, data);
|
||||
break;
|
||||
|
||||
case BrowserAction.SwitchToTab:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using OpenQA.Selenium;
|
||||
using RuriLib.Functions.Files;
|
||||
using RuriLib.LS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -299,7 +300,7 @@ namespace RuriLib
|
||||
|
||||
case ElementAction.Screenshot:
|
||||
var image = GetElementScreenShot(data.Driver, element);
|
||||
SaveScreenshot(image, data);
|
||||
Files.SaveScreenshot(image, data);
|
||||
break;
|
||||
|
||||
case ElementAction.SwitchToFrame:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Extreme.Net;
|
||||
using Newtonsoft.Json;
|
||||
using RuriLib.Functions.Requests;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
@@ -46,7 +47,7 @@ namespace RuriLib.CaptchaServices
|
||||
// Create task
|
||||
HttpRequest request = new HttpRequest();
|
||||
request.AddHeader(HttpHeader.Accept, "application/json");
|
||||
var content = new Extreme.Net.MultipartContent(BlockRequest.GenerateMultipartBoundary());
|
||||
var content = new Extreme.Net.MultipartContent(Request.GenerateMultipartBoundary());
|
||||
content.Add(new Extreme.Net.StringContent(User), "username");
|
||||
content.Add(new Extreme.Net.StringContent(Pass), "password");
|
||||
content.Add(new Extreme.Net.StringContent("4"), "type");
|
||||
@@ -97,7 +98,7 @@ namespace RuriLib.CaptchaServices
|
||||
// Create task
|
||||
HttpRequest request = new HttpRequest();
|
||||
request.AddHeader(HttpHeader.Accept, "application/json");
|
||||
Extreme.Net.MultipartContent content = new Extreme.Net.MultipartContent(BlockRequest.GenerateMultipartBoundary());
|
||||
Extreme.Net.MultipartContent content = new Extreme.Net.MultipartContent(Request.GenerateMultipartBoundary());
|
||||
content.Add(new Extreme.Net.StringContent(User), "username");
|
||||
content.Add(new Extreme.Net.StringContent(Pass), "password");
|
||||
content.Add(new Extreme.Net.StringContent($"base64:{GetBase64(bitmap, ImageFormat.Jpeg)}"), "captchafile");
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.Functions.Files
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to work with files.
|
||||
/// </summary>
|
||||
public static class Files
|
||||
{
|
||||
/// <summary>
|
||||
/// Saves a Selenium screenshot to a file with automatically generated name.
|
||||
/// </summary>
|
||||
/// <param name="screenshot">The Selenium screenshot</param>
|
||||
/// <param name="data">The BotData used for path creation</param>
|
||||
public static void SaveScreenshot(OpenQA.Selenium.Screenshot screenshot, BotData data)
|
||||
{
|
||||
var path = MakeScreenshotPath(data);
|
||||
data.Screenshots.Add(path);
|
||||
screenshot.SaveAsFile(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a screenshot to a file with automatically generated name.
|
||||
/// </summary>
|
||||
/// <param name="screenshot">The bitmap image</param>
|
||||
/// <param name="data">The BotData used for path creation</param>
|
||||
public static void SaveScreenshot(Bitmap screenshot, BotData data)
|
||||
{
|
||||
var path = MakeScreenshotPath(data);
|
||||
data.Screenshots.Add(path);
|
||||
screenshot.Save(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a stream to a file with automatically generated name.
|
||||
/// </summary>
|
||||
/// <param name="stream">The input stream</param>
|
||||
/// <param name="data">The BotData used for path creation</param>
|
||||
public static void SaveScreenshot(MemoryStream stream, BotData data)
|
||||
{
|
||||
var path = MakeScreenshotPath(data);
|
||||
using (var fileStream = File.Create(path)) { stream.CopyTo(fileStream); }
|
||||
data.Screenshots.Add(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the path for the screenshot file.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData for path creation</param>
|
||||
/// <returns>The path of the file to save the screenshot to</returns>
|
||||
private static string MakeScreenshotPath(BotData data)
|
||||
{
|
||||
var folderName = MakeValidFileName(data.ConfigSettings.Name);
|
||||
var originalFilename = MakeValidFileName(data.Data.Data);
|
||||
// Check if you have to make the folder
|
||||
if (!Directory.Exists($"Screenshots\\{folderName}")) Directory.CreateDirectory($"Screenshots\\{folderName}");
|
||||
|
||||
// Save the file inside the folder
|
||||
var filename = GetFirstAvailableFileName($"Screenshots\\{folderName}\\", originalFilename, "bmp");
|
||||
return $"Screenshots\\{folderName}\\{filename}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first available name in the given folder by incrementing a number at the end of the filename.
|
||||
/// </summary>
|
||||
/// <param name="basePath">The path to the folder</param>
|
||||
/// <param name="fileName">The name of the file without numbers at the end</param>
|
||||
/// <param name="extension">The extension of the file</param>
|
||||
/// <returns>The first available filename (including extension)</returns>
|
||||
public static string GetFirstAvailableFileName(string basePath, string fileName, string extension)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; File.Exists(basePath + fileName + i + "." + extension); i++) { }
|
||||
return fileName + i + "." + extension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixes the filename to be compatible with the filesystem indicization.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file</param>
|
||||
/// <param name="underscore">Whether to replace the unallowed characters with an underscore instead of removing them</param>
|
||||
/// <returns>The valid filename ready to be saved to disk</returns>
|
||||
public static string MakeValidFileName(string name, bool underscore = true)
|
||||
{
|
||||
string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
|
||||
string invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars);
|
||||
|
||||
return Regex.Replace(name, invalidRegStr, underscore ? "_" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
using Extreme.Net;
|
||||
using RuriLib.Functions.Formats;
|
||||
using RuriLib.Models;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using RuriLib.Functions.Files;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib.Functions.Requests
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to easily perform Extreme.NET requests.
|
||||
/// </summary>
|
||||
public class Request
|
||||
{
|
||||
private HttpRequest request = new HttpRequest();
|
||||
private HttpContent content = null;
|
||||
private Dictionary<string, string> oldCookies = new Dictionary<string, string>();
|
||||
private int timeout = 60000;
|
||||
private string url = "";
|
||||
private string contentType = "";
|
||||
private string authorization = "";
|
||||
|
||||
private HttpResponse response = null;
|
||||
private bool hasContentLength = true;
|
||||
private bool isGZipped = false;
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the request options.
|
||||
/// </summary>
|
||||
/// <param name="settings">The RuriLib settings</param>
|
||||
/// <param name="autoRedirect">Whether to perform automatic redirection</param>
|
||||
/// <param name="acceptEncoding"></param>
|
||||
/// <param name="maxRedirects"></param>
|
||||
/// <returns></returns>
|
||||
public Request Setup(RLSettingsViewModel settings, bool autoRedirect = true, int maxRedirects = 8, bool acceptEncoding = true)
|
||||
{
|
||||
// Setup options
|
||||
timeout = settings.General.RequestTimeout * 1000;
|
||||
request.IgnoreProtocolErrors = true;
|
||||
request.AllowAutoRedirect = autoRedirect;
|
||||
request.EnableEncodingContent = acceptEncoding;
|
||||
request.ReadWriteTimeout = timeout;
|
||||
request.ConnectTimeout = timeout;
|
||||
request.KeepAlive = true;
|
||||
request.MaximumAutomaticRedirections = maxRedirects;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a standard content for the request.
|
||||
/// </summary>
|
||||
/// <param name="postData">The content string</param>
|
||||
/// <param name="contentType">The content type</param>
|
||||
/// <param name="method">The HTTP method</param>
|
||||
/// <param name="encodeContent">Whether to URLencode the content automatically</param>
|
||||
/// <param name="log">The log (if any)</param>
|
||||
/// <returns>The request itself</returns>
|
||||
public Request SetStandardContent(string postData, string contentType,
|
||||
HttpMethod method = HttpMethod.POST, bool encodeContent = false, List<LogEntry> log = null)
|
||||
{
|
||||
this.contentType = contentType;
|
||||
var pData = Regex.Replace(postData, @"(?<!\\)\\n", Environment.NewLine).Replace(@"\\n", @"\n");
|
||||
|
||||
if (CanContainBody(method))
|
||||
{
|
||||
if (encodeContent)
|
||||
{
|
||||
// Very dirty but it works
|
||||
Random rand = new Random();
|
||||
var nonce = rand.Next(1000000, 9999999);
|
||||
pData = pData.Replace("&", $"{nonce}&{nonce}").Replace("=", $"{nonce}={nonce}");
|
||||
pData = string.Join("", BlockFunction.SplitInChunks(pData, 2080)
|
||||
.Select(s => Uri.EscapeDataString(s)))
|
||||
.Replace($"{nonce}%26{nonce}", "&").Replace($"{nonce}%3D{nonce}", "=");
|
||||
}
|
||||
|
||||
content = new StringContent(pData);
|
||||
content.ContentType = contentType;
|
||||
|
||||
if (log != null) log.Add(new LogEntry($"Post Data: {pData}", Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the authorization header for basic authorization.
|
||||
/// </summary>
|
||||
/// <param name="username">The username</param>
|
||||
/// <param name="password">The password</param>
|
||||
/// <returns>The request itself</returns>
|
||||
public Request SetBasicAuth(string username, string password)
|
||||
{
|
||||
authorization = "Basic " + (username + ":" + password).ToBase64();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a multipart content collection for the request.
|
||||
/// </summary>
|
||||
/// <param name="contents">A collection of multipart contents</param>
|
||||
/// <param name="boundary">A boundary (if empty, one will be generated automatically)</param>
|
||||
/// <param name="log">The log (if any)</param>
|
||||
/// <returns>The request itself</returns>
|
||||
public Request SetMultipartContent(IEnumerable<MultipartContent> contents, string boundary = "", List<LogEntry> log = null)
|
||||
{
|
||||
var bdry = boundary != "" ? boundary : GenerateMultipartBoundary();
|
||||
content = new Extreme.Net.MultipartContent(bdry);
|
||||
var mContent = content as Extreme.Net.MultipartContent;
|
||||
|
||||
if (log != null)
|
||||
{
|
||||
log.Add(new LogEntry($"Content-Type: multipart/form-data; boundary={bdry}", Colors.MediumTurquoise));
|
||||
log.Add(new LogEntry("Multipart Data:", Colors.MediumTurquoise));
|
||||
log.Add(new LogEntry(bdry, Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
foreach (var c in contents)
|
||||
{
|
||||
if (c.Type == MultipartContentType.String)
|
||||
{
|
||||
mContent.Add(new StringContent(c.Value), c.Name);
|
||||
if (log != null) log.Add(new LogEntry($"Content-Disposition: form-data; name=\"{c.Name}\"{Environment.NewLine}{Environment.NewLine}{c.Value}", Colors.MediumTurquoise));
|
||||
}
|
||||
else if (c.Type == MultipartContentType.File)
|
||||
{
|
||||
mContent.Add(new FileContent(c.Value), c.Name, c.Value, c.ContentType);
|
||||
if (log != null) log.Add(new LogEntry($"Content-Disposition: form-data; name=\"{c.Name}\"; filename=\"{c.Value}\"{Environment.NewLine}Content-Type: {c.ContentType}{Environment.NewLine}{Environment.NewLine}[FILE CONTENT OMITTED]", Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
if (log != null) log.Add(new LogEntry(bdry, Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a proxy to be used during the request.
|
||||
/// </summary>
|
||||
/// <param name="proxy">The proxy</param>
|
||||
/// <returns>The request itself</returns>
|
||||
public Request SetProxy(CProxy proxy)
|
||||
{
|
||||
request.Proxy = proxy.GetClient();
|
||||
|
||||
request.Proxy.ReadWriteTimeout = timeout;
|
||||
request.Proxy.ConnectTimeout = timeout;
|
||||
request.Proxy.Username = proxy.Username;
|
||||
request.Proxy.Password = proxy.Password;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the cookies to be sent in the request.
|
||||
/// </summary>
|
||||
/// <param name="cookies">The cookie dictionary</param>
|
||||
/// <param name="log">The log (if any)</param>
|
||||
/// <returns>The request itself</returns>
|
||||
public Request SetCookies(Dictionary<string, string> cookies, List<LogEntry> log = null)
|
||||
{
|
||||
oldCookies = cookies;
|
||||
request.Cookies = new CookieDictionary();
|
||||
|
||||
foreach (var cookie in cookies)
|
||||
{
|
||||
request.Cookies.Add(cookie.Key, cookie.Value);
|
||||
log.Add(new LogEntry($"{cookie.Key}: {cookie.Value}", Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the headers to be sent in the request.
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers dictionary</param>
|
||||
/// <param name="acceptEncoding">Whether to set the Accept-Encoding header automatically</param>
|
||||
/// <param name="log">The log (if any)</param>
|
||||
/// <returns>The request itself</returns>
|
||||
public Request SetHeaders(Dictionary<string, string> headers, bool acceptEncoding = true, List<LogEntry> log = null)
|
||||
{
|
||||
// Set headers
|
||||
foreach (var header in headers)
|
||||
{
|
||||
try
|
||||
{
|
||||
var replacedKey = header.Key.Replace("-", "").ToLower(); // Used to compare with the HttpHeader enum
|
||||
|
||||
if (replacedKey == "contenttype" && content != null) { continue; } // Disregard additional Content-Type headers
|
||||
if (replacedKey == "acceptencoding" && acceptEncoding) { continue; } // Disregard additional Accept-Encoding headers
|
||||
// else if (fixedNames.Contains(replacedKey)) request.AddHeader((HttpHeader)Enum.Parse(typeof(HttpHeader), replacedKey, true), val);
|
||||
else request.AddHeader(header.Key, header.Value);
|
||||
|
||||
if (log != null) log.Add(new LogEntry($"{header.Key}: {header.Value}", Colors.MediumTurquoise));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Add the authorization header on a Basic Auth request
|
||||
if (authorization != "")
|
||||
{
|
||||
request.AddHeader("Authorization", authorization);
|
||||
if (log != null) log.Add(new LogEntry($"Authorization: {authorization}", Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
// Add the content-type header
|
||||
if (contentType != "")
|
||||
{
|
||||
log.Add(new LogEntry($"Content-Type: {contentType}", Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a request.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL</param>
|
||||
/// <param name="method">The HTTP method</param>
|
||||
/// <param name="ignoreErrors">Whether to ignore response errors</param>
|
||||
/// <param name="log">The log (if any)</param>
|
||||
/// <returns>A 4-tuple containing Address, Response code, Headers and Cookies.</returns>
|
||||
public (string, string, Dictionary<string, string>, Dictionary<string, string>) Perform(string url, HttpMethod method,
|
||||
bool ignoreErrors = false, List<LogEntry> log = null)
|
||||
{
|
||||
var address = "";
|
||||
var responseCode = "0";
|
||||
var headers = new Dictionary<string, string>();
|
||||
var cookies = oldCookies;
|
||||
|
||||
try
|
||||
{
|
||||
// Get response
|
||||
HttpResponse response = request.Raw(method, url, content);
|
||||
|
||||
// Get address
|
||||
address = response.Address.ToString();
|
||||
if (log != null) log.Add(new LogEntry("Address: " + address, Colors.Cyan));
|
||||
|
||||
// Get code
|
||||
responseCode = ((int)response.StatusCode).ToString();
|
||||
if (log != null) log.Add(new LogEntry($"Response code: {responseCode} ({response.StatusCode})", Colors.Cyan));
|
||||
|
||||
// Get headers
|
||||
if (log != null) log.Add(new LogEntry("Received headers:", Colors.DeepPink));
|
||||
var headersList = new List<KeyValuePair<string, string>>();
|
||||
var receivedHeaders = response.EnumerateHeaders();
|
||||
while (receivedHeaders.MoveNext())
|
||||
{
|
||||
var header = receivedHeaders.Current;
|
||||
headersList.Add(new KeyValuePair<string, string>(header.Key, header.Value));
|
||||
if (log != null) log.Add(new LogEntry($"{header.Key}: {header.Value}", Colors.LightPink));
|
||||
}
|
||||
headers = headersList.ToDictionary(h => h.Key, h => h.Value);
|
||||
hasContentLength = headers.ContainsKey("Content-Length");
|
||||
isGZipped = headers.ContainsKey("Content-Encoding") && headers["Content-Encoding"].Contains("gzip");
|
||||
|
||||
// Get cookies
|
||||
cookies = response.Cookies;
|
||||
if (log != null)
|
||||
{
|
||||
log.Add(new LogEntry("Received cookies:", Colors.Goldenrod));
|
||||
foreach (var cookie in response.Cookies)
|
||||
{
|
||||
// If the cookie was already present before, don't log it
|
||||
if (oldCookies.ContainsKey(cookie.Key) && oldCookies[cookie.Key] == cookie.Value) continue;
|
||||
log.Add(new LogEntry($"{cookie.Key}: {cookie.Value}", Colors.LightGoldenrodYellow));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (log != null) log.Add(new LogEntry(ex.Message, Colors.White));
|
||||
|
||||
if (ex.GetType() == typeof(HttpException))
|
||||
{
|
||||
responseCode = ((HttpException)ex).HttpStatusCode.ToString();
|
||||
if (log != null) log.Add(new LogEntry("Status code: " + responseCode, Colors.Cyan));
|
||||
}
|
||||
|
||||
if (!ignoreErrors) throw;
|
||||
}
|
||||
|
||||
return (address, responseCode, headers, cookies);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the response to a string.
|
||||
/// </summary>
|
||||
/// <param name="readResponseSource"></param>
|
||||
/// <param name="headers">The headers, to add the Content-Length header (if needed)</param>
|
||||
/// <param name="log">The log (if any)</param>
|
||||
/// <returns>The response source as a string</returns>
|
||||
public string SaveString(bool readResponseSource, Dictionary<string, string> headers = null, List<LogEntry> log = null)
|
||||
{
|
||||
var source = "";
|
||||
var responseString = response.ToString();
|
||||
|
||||
if (log != null) log.Add(new LogEntry("Response Source:", Colors.Green));
|
||||
if (readResponseSource)
|
||||
{
|
||||
source = responseString;
|
||||
if (log != null) log.Add(new LogEntry(source, Colors.GreenYellow));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log != null) log.Add(new LogEntry("[SKIPPED]", Colors.GreenYellow));
|
||||
}
|
||||
|
||||
if (!hasContentLength && headers != null)
|
||||
{
|
||||
if (isGZipped)
|
||||
{
|
||||
headers["Content-Length"] = GZip.Zip(responseString).Length.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
headers["Content-Length"] = responseString.Length.ToString();
|
||||
}
|
||||
|
||||
if (log != null) log.Add(new LogEntry($"Calculated header: Content-Length: {headers["Content-Length"]}", Colors.LightPink));
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the response to a MemoryStream.
|
||||
/// </summary>
|
||||
/// <returns>The MemoryStream</returns>
|
||||
public MemoryStream GetResponseStream()
|
||||
{
|
||||
return response.ToMemoryStream();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the response content to a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path on disk</param>
|
||||
/// <param name="log">The log (if any)</param>
|
||||
public void SaveFile(string path, List<LogEntry> log = null)
|
||||
{
|
||||
var dirName = Path.GetDirectoryName(path);
|
||||
if (dirName != "") dirName += Path.DirectorySeparatorChar.ToString();
|
||||
var fileName = Path.GetFileNameWithoutExtension(path);
|
||||
var fileExtension = Path.GetExtension(path);
|
||||
var sanitizedPath = $"{dirName}{Files.Files.MakeValidFileName(fileName)}{fileExtension}";
|
||||
using (var stream = File.Create(sanitizedPath)) { response.ToMemoryStream().CopyTo(stream); } // Read the stream
|
||||
if (log != null) log.Add(new LogEntry("File saved as " + sanitizedPath, Colors.Green));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random string to be used for boundary.
|
||||
/// </summary>
|
||||
/// <returns>The random 16-character string</returns>
|
||||
internal static string GenerateMultipartBoundary()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Random random = new Random();
|
||||
char ch;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
|
||||
builder.Append(ch);
|
||||
}
|
||||
return $"------WebKitFormBoundary{builder.ToString().ToLower()}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an HTTP method can have a body.
|
||||
/// </summary>
|
||||
/// <param name="method">The HTTP method</param>
|
||||
/// <returns>True if the method allows a body</returns>
|
||||
public static bool CanContainBody(HttpMethod method)
|
||||
{
|
||||
return method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.DELETE;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Multipart Content
|
||||
/// </summary>
|
||||
public struct MultipartContent
|
||||
{
|
||||
/// <summary>The type of multipart content.</summary>
|
||||
public MultipartContentType Type;
|
||||
|
||||
/// <summary>The name of the multipart content.</summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>The value of the multipart content (a string value or a file path).</summary>
|
||||
public string Value;
|
||||
|
||||
/// <summary>The Content-Type of the file content.</summary>
|
||||
public string ContentType;
|
||||
}
|
||||
}
|
||||
@@ -188,6 +188,7 @@
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
@@ -216,6 +217,8 @@
|
||||
<Compile Include="Functions\Crypto\Crypto.cs" />
|
||||
<Compile Include="Functions\Download\Download.cs" />
|
||||
<Compile Include="Functions\Encoding\Encode.cs" />
|
||||
<Compile Include="Functions\Files\Files.cs" />
|
||||
<Compile Include="Functions\Requests\Request.cs" />
|
||||
<Compile Include="Functions\Time\Time.cs" />
|
||||
<Compile Include="GZip.cs" />
|
||||
<Compile Include="LoliScript\Exceptions\BlockProcessingException.cs" />
|
||||
|
||||
Reference in New Issue
Block a user