Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# GameVault App Changelog

## 1.17.5
Recommended Gamevault Server Version: `v16.1.2`
### Changes
- Bug fix: The required request headers for download resume and database backup were not sent.
- Bug fix: You couldn't enter spaces for the password in the login window.
- Bug fix: The session tokens were not properly persistent

## 1.17.4
Recommended Gamevault Server Version: `v16.1.1`
### Changes
Expand Down
2 changes: 1 addition & 1 deletion gamevault/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
[assembly: AssemblyVersion("1.17.4.0")]
[assembly: AssemblyVersion("1.17.5.0")]
[assembly: AssemblyCopyright("© Phalcode™. All Rights Reserved.")]
#if DEBUG
[assembly: XmlnsDefinition("debug-mode", "Namespace")]
Expand Down
4 changes: 2 additions & 2 deletions gamevault/Helper/Integrations/SaveGameHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ internal async Task<string> RestoreBackup(int gameId, string installationDir)
string[] auth = WebHelper.GetCredentials();

string url = @$"{SettingsViewModel.Instance.ServerUrl}/api/savefiles/user/{LoginManager.Instance.GetCurrentUser()!.ID}/game/{gameId}";
using (HttpResponseMessage response = await WebHelper.GetAsync(@$"{SettingsViewModel.Instance.ServerUrl}/api/savefiles/user/{LoginManager.Instance.GetCurrentUser()!.ID}/game/{gameId}", HttpCompletionOption.ResponseHeadersRead))
using (HttpResponseMessage response = await WebHelper.GetAsync(@$"{SettingsViewModel.Instance.ServerUrl}/api/savefiles/user/{LoginManager.Instance.GetCurrentUser()!.ID}/game/{gameId}", null, HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode();
string fileName = response.Content.Headers.ContentDisposition.FileName.Split('_')[1].Split('.')[0];
Expand Down Expand Up @@ -356,7 +356,7 @@ private async Task<bool> UploadSavegame(string saveFilePath, int gameId, string
string installationId = GetGameInstallationId(installationDir);
using (MemoryStream memoryStream = await FileToMemoryStreamAsync(saveFilePath))
{
await WebHelper.UploadFileAsync(@$"{SettingsViewModel.Instance.ServerUrl}/api/savefiles/user/{LoginManager.Instance.GetCurrentUser()!.ID}/game/{gameId}", memoryStream, "x.zip", new RequestHeader[] { new RequestHeader() { Name = "X-Installation-Id", Value = installationId } });
await WebHelper.UploadFileAsync(@$"{SettingsViewModel.Instance.ServerUrl}/api/savefiles/user/{LoginManager.Instance.GetCurrentUser()!.ID}/game/{gameId}", memoryStream, "x.zip", new List<RequestHeader> { new RequestHeader() { Name = "X-Installation-Id", Value = installationId } });
}
}
catch
Expand Down
7 changes: 1 addition & 6 deletions gamevault/Helper/PasswordBoxAttachedProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,8 @@ private static void OnPreviewKeyDown(object sender, KeyEventArgs e)
if (sender is not TextBox textBox || !GetIsPassword(textBox))
return;

// Prevent spaces in password
if (e.Key == Key.Space)
{
e.Handled = true;
}
// Handle Ctrl+A select all
else if (e.Key == Key.A && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
if (e.Key == Key.A && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
textBox.SelectAll();
e.Handled = true;
Expand Down
34 changes: 11 additions & 23 deletions gamevault/Helper/Web/HttpClientDownloadWithProgress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@

namespace gamevault.Helper
{
public class HttpClientDownloadWithProgress : IDisposable
public class HttpClientDownloadWithProgress
{
private readonly string DownloadUrl;
private readonly string DestinationFolderPath;
private string FileName;
private string FallbackFileName;
private KeyValuePair<string, string>? AdditionalHeader;
private Dictionary<string, string>? AdditionalHeader;
private bool Cancelled = false;
private bool Paused = false;
private long ResumePosition = -1;
private long PreResumeSize = -1;
private DateTime LastTime;
private HttpClient HttpClient;


public delegate void ProgressChangedHandler(long totalFileSize, long currentBytesDownloaded, long totalBytesDownloaded, double? progressPercentage, long resumePosition);

public event ProgressChangedHandler ProgressChanged;

public HttpClientDownloadWithProgress(string downloadUrl, string destinationFolderPath, string fallbackFileName, KeyValuePair<string, string>? additionalHeader = null)
public HttpClientDownloadWithProgress(string downloadUrl, string destinationFolderPath, string fallbackFileName, Dictionary<string, string>? additionalHeader = null)
{
DownloadUrl = downloadUrl;
DestinationFolderPath = destinationFolderPath;
Expand All @@ -42,8 +42,7 @@ public HttpClientDownloadWithProgress(string downloadUrl, string destinationFold

public async Task StartDownload(bool tryResume = false)
{
HttpClient = new HttpClient { Timeout = TimeSpan.FromDays(7) };
CreateHeader();

if (tryResume)
{
InitResume();
Expand All @@ -55,18 +54,9 @@ public async Task StartDownload(bool tryResume = false)
File.Delete($"{DestinationFolderPath}\\gamevault-metadata");
}

using (HttpResponseMessage response = await WebHelper.GetAsync(DownloadUrl, HttpCompletionOption.ResponseHeadersRead))
using (HttpResponseMessage response = await WebHelper.GetAsync(DownloadUrl, AdditionalHeader, HttpCompletionOption.ResponseHeadersRead))
await DownloadFileFromHttpResponseMessage(response);
}
private void CreateHeader()
{
string[] auth = WebHelper.GetCredentials();
HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{auth[0]}:{auth[1]}")));
HttpClient.DefaultRequestHeaders.Add("User-Agent", $"GameVault/{SettingsViewModel.Instance.Version}");

if (AdditionalHeader != null)
HttpClient.DefaultRequestHeaders.Add(AdditionalHeader.Value.Key, AdditionalHeader.Value.Value);
}
private void InitResume()
{
string resumeData = Preferences.Get(AppConfigKey.DownloadProgress, $"{DestinationFolderPath}\\gamevault-metadata");
Expand All @@ -77,7 +67,11 @@ private void InitResume()
string[] resumeDataToProcess = resumeData.Split(";");
ResumePosition = long.Parse(resumeDataToProcess[0]);
PreResumeSize = long.Parse(resumeDataToProcess[1]);
HttpClient.DefaultRequestHeaders.Range = new RangeHeaderValue(long.Parse(resumeDataToProcess[0]), null);
if (AdditionalHeader == null)
{
AdditionalHeader = new Dictionary<string, string>();
}
AdditionalHeader?.Add("Range", $"bytes={ResumePosition}-");
}
catch { }
}
Expand Down Expand Up @@ -206,17 +200,11 @@ public void Cancel()
return;
}
Cancelled = true;
Dispose();
}
public void Pause()
{
Paused = true;
Cancelled = true;
Dispose();
}
public void Dispose()
{
HttpClient?.Dispose();
}
}
}
15 changes: 10 additions & 5 deletions gamevault/Helper/Web/OAuthHttpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public void InjectTokens(string accessToken, string refreshToken)
{
_accessToken = accessToken;
_refreshToken = refreshToken;
try
{
nextTokenRefresh = GetNextTokenRefresh(_accessToken);
}
catch { }
}
public string GetRefreshToken()
{
Expand Down Expand Up @@ -60,7 +65,7 @@ public async Task<bool> LoginBasicAuthAsync(string username, string password)
return true;
}

private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, RequestHeader[]? additionalHeaders = null, HttpCompletionOption option = HttpCompletionOption.ResponseContentRead)
private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, List<RequestHeader>? additionalHeaders = null, HttpCompletionOption option = HttpCompletionOption.ResponseContentRead)
{
if (string.IsNullOrEmpty(_accessToken))
{
Expand All @@ -82,16 +87,16 @@ private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, Re
return await _httpClient.SendAsync(request, option);
}

public Task<HttpResponseMessage> GetAsync(string url, RequestHeader[]? additionalHeaders = null, HttpCompletionOption option = HttpCompletionOption.ResponseContentRead) =>
public Task<HttpResponseMessage> GetAsync(string url, List<RequestHeader>? additionalHeaders = null, HttpCompletionOption option = HttpCompletionOption.ResponseContentRead) =>
SendAsync(new HttpRequestMessage(HttpMethod.Get, url), additionalHeaders, option);

public Task<HttpResponseMessage> PostAsync(string url, HttpContent content, RequestHeader[]? additionalHeaders = null) =>
public Task<HttpResponseMessage> PostAsync(string url, HttpContent content, List<RequestHeader>? additionalHeaders = null) =>
SendAsync(new HttpRequestMessage(HttpMethod.Post, url) { Content = content }, additionalHeaders);

public Task<HttpResponseMessage> PutAsync(string url, HttpContent content, RequestHeader[]? additionalHeaders = null) =>
public Task<HttpResponseMessage> PutAsync(string url, HttpContent content, List<RequestHeader>? additionalHeaders = null) =>
SendAsync(new HttpRequestMessage(HttpMethod.Put, url) { Content = content }, additionalHeaders);

public Task<HttpResponseMessage> DeleteAsync(string url, RequestHeader[]? additionalHeaders = null) =>
public Task<HttpResponseMessage> DeleteAsync(string url, List<RequestHeader>? additionalHeaders = null) =>
SendAsync(new HttpRequestMessage(HttpMethod.Delete, url), additionalHeaders);

private async Task<bool> RefreshTokenAsync()
Expand Down
41 changes: 23 additions & 18 deletions gamevault/Helper/Web/WebHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal class WebHelper
{
DefaultRequestHeaders = { { "User-Agent", "GameVault" } }
};
private static RequestHeader[] AdditionalRequestHeaders;
private static List<RequestHeader> AdditionalRequestHeaders = new List<RequestHeader>();
static WebHelper() { }
internal static void SetCredentials(string serverUrl, string username, string password)
{
Expand Down Expand Up @@ -47,7 +47,7 @@ internal static string GetRefreshToken()
{
return HttpClient.GetRefreshToken();
}
internal static void SetAdditionalRequestHeaders(RequestHeader[] additionalRequestHeaders)
internal static void SetAdditionalDefaultRequestHeaders(List<RequestHeader> additionalRequestHeaders)
{
AdditionalRequestHeaders = additionalRequestHeaders;

Expand All @@ -62,40 +62,45 @@ internal static void SetAdditionalRequestHeaders(RequestHeader[] additionalReque
internal static async Task<string> BaseGetAsync(string url)
{
var response = await BaseHttpClient.GetAsync(url);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
return await response.Content.ReadAsStringAsync();
}
internal static async Task<string> BasePostAsync(string url, string payload)
{
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await BaseHttpClient.PostAsync(url, content);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
return await response.Content.ReadAsStringAsync();
}
internal static async Task<string> BaseSendRequest(HttpRequestMessage request)
{
var response = await BaseHttpClient.SendAsync(request);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
return await response.Content.ReadAsStringAsync();
}
#endregion
internal static async Task<string> GetAsync(string url)
{
var response = await HttpClient.GetAsync(url, AdditionalRequestHeaders);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
return await response.Content.ReadAsStringAsync();
}
internal static async Task<HttpResponseMessage> GetAsync(string url, HttpCompletionOption option = HttpCompletionOption.ResponseContentRead)
internal static async Task<HttpResponseMessage> GetAsync(string url, Dictionary<string, string>? additionalManualRequestHeaders = null, HttpCompletionOption option = HttpCompletionOption.ResponseContentRead)
{
var response = await HttpClient.GetAsync(url, AdditionalRequestHeaders, option);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
List<RequestHeader> additionalHeaders = null;
if (additionalManualRequestHeaders is { Count: > 0 })
{
additionalHeaders = additionalManualRequestHeaders.Select(h => new RequestHeader { Name = h.Key, Value = h.Value }).Concat(AdditionalRequestHeaders).ToList();
}
var response = await HttpClient.GetAsync(url, additionalHeaders ?? AdditionalRequestHeaders, option);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
return response;
}
internal static async Task<string> PostAsync(string url, string payload)
{
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await HttpClient.PostAsync(url, content, AdditionalRequestHeaders);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
return await response.Content.ReadAsStringAsync();
}

Expand All @@ -109,20 +114,20 @@ internal static async Task<string> PutAsync(string url, string payload)
internal static async Task<string> DeleteAsync(string url)
{
var response = await HttpClient.DeleteAsync(url, AdditionalRequestHeaders);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
return await response.Content.ReadAsStringAsync();
}
public static async Task DownloadImageFromUrlAsync(string imageUrl, string cacheFile)
{
var response = await HttpClient.GetAsync(imageUrl, AdditionalRequestHeaders);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
var imageBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync(cacheFile, imageBytes);
}
public static async Task<BitmapImage> DownloadImageFromUrlAsync(string imageUrl)
{
var response = await HttpClient.GetAsync(imageUrl, AdditionalRequestHeaders);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
var imageData = await response.Content.ReadAsByteArrayAsync();
using (var memoryStream = new MemoryStream(imageData))
{
Expand All @@ -135,13 +140,13 @@ public static async Task<BitmapImage> DownloadImageFromUrlAsync(string imageUrl)
return bitmap;
}
}
public static async Task<string> UploadFileAsync(string apiUrl, Stream imageStream, string fileName, RequestHeader[]? additionalHeaders = null)
public static async Task<string> UploadFileAsync(string apiUrl, Stream imageStream, string fileName, List<RequestHeader>? additionalHeaders = null)
{
//Mix request headers
RequestHeader[]? mixedHeaders = null;
List<RequestHeader>? mixedHeaders = null;
if (additionalHeaders != null && AdditionalRequestHeaders != null)
{
mixedHeaders = AdditionalRequestHeaders.Concat(additionalHeaders).ToArray();
mixedHeaders = AdditionalRequestHeaders.Concat(additionalHeaders).ToList();
}
else if (additionalHeaders == null)
{
Expand All @@ -158,7 +163,7 @@ public static async Task<string> UploadFileAsync(string apiUrl, Stream imageStre
imageContent.Headers.ContentType = new MediaTypeHeaderValue(mimeType);
formData.Add(imageContent, "file", fileName);
var response = await HttpClient.PostAsync(apiUrl, formData, mixedHeaders);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
await WebExceptionHelper.EnsureSuccessStatusCode(response);
var responseContent = await response.Content.ReadAsStringAsync();
return responseContent;
}
Expand All @@ -184,7 +189,7 @@ public static string RemoveSpecialCharactersFromUrl(string url)
}
return sb.ToString();
}

}
}

Expand Down
8 changes: 3 additions & 5 deletions gamevault/UserControls/GameDownloadUserControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,12 @@ private async Task DownloadGame(bool tryResume = false)
ViewModel.DownloadFailedVisibility = System.Windows.Visibility.Hidden;

if (!Directory.Exists(m_DownloadPath)) { Directory.CreateDirectory(m_DownloadPath); }
KeyValuePair<string, string>? header = null;
Dictionary<string,string> additionalRequestHeaders = new Dictionary<string,string>();
if (SettingsViewModel.Instance.DownloadLimit > 0)
{
header = new KeyValuePair<string, string>("X-Download-Speed-Limit", SettingsViewModel.Instance.DownloadLimit.ToString());
additionalRequestHeaders.Add("X-Download-Speed-Limit", SettingsViewModel.Instance.DownloadLimit.ToString());
}
client = new HttpClientDownloadWithProgress($"{SettingsViewModel.Instance.ServerUrl}/api/games/{ViewModel.Game.ID}/download", m_DownloadPath, Path.GetFileName(ViewModel.Game.Path), header);
client = new HttpClientDownloadWithProgress($"{SettingsViewModel.Instance.ServerUrl}/api/games/{ViewModel.Game.ID}/download", m_DownloadPath, Path.GetFileName(ViewModel.Game.Path), additionalRequestHeaders);
client.ProgressChanged += DownloadProgress;
startTime = DateTime.Now;
downloadSpeedCalc = new DownloadSpeedCalculator();
Expand All @@ -195,7 +195,6 @@ private async Task DownloadGame(bool tryResume = false)
catch (Exception ex)
{
IsDownloadActive = false;
client.Dispose();
ViewModel.State = $"Error: '{ex.Message}'";
ViewModel.DownloadUIVisibility = System.Windows.Visibility.Hidden;
ViewModel.DownloadFailedVisibility = System.Windows.Visibility.Visible;
Expand Down Expand Up @@ -314,7 +313,6 @@ private void DownloadCompleted()

UpdateDataSizeUI();
ViewModel.DownloadUIVisibility = System.Windows.Visibility.Hidden;
client.Dispose();
IsDownloadActive = false;
ViewModel.State = "Downloaded";
uiBtnExtract.IsEnabled = true;
Expand Down
Loading
Loading