mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 06:46:04 +02:00
Switch from sha256.txt to digest
This commit is contained in:
@@ -4,9 +4,9 @@ namespace VRCX;
|
|||||||
|
|
||||||
public partial class AppApi
|
public partial class AppApi
|
||||||
{
|
{
|
||||||
public async Task DownloadUpdate(string fileUrl, string fileName, string hashUrl, int downloadSize)
|
public async Task DownloadUpdate(string fileUrl, string hashString, int downloadSize)
|
||||||
{
|
{
|
||||||
await Update.DownloadUpdate(fileUrl, fileName, hashUrl, downloadSize);
|
await Update.DownloadUpdate(fileUrl, hashString, downloadSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CancelUpdate()
|
public void CancelUpdate()
|
||||||
|
|||||||
+31
-54
@@ -26,7 +26,6 @@ namespace VRCX
|
|||||||
private static readonly string VrcxSetupExecutable = Path.Join(Program.AppDataDirectory, "VRCX_Setup.exe");
|
private static readonly string VrcxSetupExecutable = Path.Join(Program.AppDataDirectory, "VRCX_Setup.exe");
|
||||||
private static readonly string UpdateExecutable = Path.Join(Program.AppDataDirectory, "update.exe");
|
private static readonly string UpdateExecutable = Path.Join(Program.AppDataDirectory, "update.exe");
|
||||||
private static readonly string TempDownload = Path.Join(Program.AppDataDirectory, "tempDownload");
|
private static readonly string TempDownload = Path.Join(Program.AppDataDirectory, "tempDownload");
|
||||||
private static readonly string HashLocation = Path.Join(Program.AppDataDirectory, "sha256sum.txt");
|
|
||||||
private static readonly HttpClient httpClient;
|
private static readonly HttpClient httpClient;
|
||||||
private static CancellationToken _cancellationToken;
|
private static CancellationToken _cancellationToken;
|
||||||
public static int UpdateProgress;
|
public static int UpdateProgress;
|
||||||
@@ -58,8 +57,6 @@ namespace VRCX
|
|||||||
if (Process.GetProcessesByName("VRCX_Setup").Length > 0)
|
if (Process.GetProcessesByName("VRCX_Setup").Length > 0)
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
|
|
||||||
if (File.Exists(HashLocation))
|
|
||||||
File.Delete(HashLocation);
|
|
||||||
if (File.Exists(TempDownload))
|
if (File.Exists(TempDownload))
|
||||||
File.Delete(TempDownload);
|
File.Delete(TempDownload);
|
||||||
if (File.Exists(VrcxSetupExecutable))
|
if (File.Exists(VrcxSetupExecutable))
|
||||||
@@ -165,52 +162,39 @@ namespace VRCX
|
|||||||
throw new Exception("Unable to extract file name from content-disposition header.");
|
throw new Exception("Unable to extract file name from content-disposition header.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task DownloadUpdate(string fileUrl, string fileName, string hashUrl, int downloadSize)
|
public static async Task DownloadUpdate(string fileUrl, string hashString, int downloadSize)
|
||||||
{
|
{
|
||||||
_cancellationToken = CancellationToken.None;
|
_cancellationToken = CancellationToken.None;
|
||||||
const int chunkSize = 8192;
|
const int chunkSize = 8192;
|
||||||
|
|
||||||
if (File.Exists(TempDownload))
|
if (File.Exists(TempDownload))
|
||||||
File.Delete(TempDownload);
|
File.Delete(TempDownload);
|
||||||
if (File.Exists(HashLocation))
|
|
||||||
File.Delete(HashLocation);
|
|
||||||
|
|
||||||
var hashesPath = await DownloadFile(hashUrl, _cancellationToken);
|
|
||||||
if (!string.IsNullOrEmpty(hashesPath))
|
|
||||||
File.Move(hashesPath, HashLocation);
|
|
||||||
|
|
||||||
await using var destination = File.OpenWrite(TempDownload);
|
await using var destination = File.OpenWrite(TempDownload);
|
||||||
using (var response = await httpClient.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, _cancellationToken))
|
using var response = await httpClient.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, _cancellationToken);
|
||||||
await using (var download = await response.Content.ReadAsStreamAsync(_cancellationToken))
|
await using var download = await response.Content.ReadAsStreamAsync(_cancellationToken);
|
||||||
|
var contentLength = response.Content.Headers.ContentLength;
|
||||||
|
var buffer = new byte[chunkSize];
|
||||||
|
long totalBytesRead = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
var contentLength = response.Content.Headers.ContentLength;
|
|
||||||
var buffer = new byte[chunkSize];
|
|
||||||
long totalBytesRead = 0;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int bytesRead = await download.ReadAsync(buffer, 0, chunkSize, _cancellationToken);
|
|
||||||
if (bytesRead == 0) break;
|
|
||||||
|
|
||||||
if (_cancellationToken.IsCancellationRequested)
|
|
||||||
throw new OperationCanceledException("Download was cancelled.");
|
|
||||||
|
|
||||||
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), _cancellationToken);
|
|
||||||
totalBytesRead += bytesRead;
|
|
||||||
|
|
||||||
if (contentLength.HasValue)
|
|
||||||
{
|
|
||||||
double percentage = Math.Round((double)totalBytesRead / contentLength.Value * 100, 2);
|
|
||||||
UpdateProgress = (int)percentage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contentLength.HasValue)
|
if (contentLength.HasValue)
|
||||||
{
|
{
|
||||||
double percentage = Math.Round((double)totalBytesRead / contentLength.Value * 100, 2);
|
var percentage = Math.Round((double)totalBytesRead / contentLength.Value * 100, 2);
|
||||||
UpdateProgress = (int)percentage;
|
UpdateProgress = (int)percentage;
|
||||||
}
|
}
|
||||||
|
var bytesRead = await download.ReadAsync(buffer.AsMemory(0, chunkSize), _cancellationToken);
|
||||||
|
if (bytesRead == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (_cancellationToken.IsCancellationRequested)
|
||||||
|
throw new OperationCanceledException("Download was cancelled.");
|
||||||
|
|
||||||
|
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), _cancellationToken);
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
destination.Close();
|
destination.Close();
|
||||||
|
|
||||||
var data = new FileInfo(TempDownload);
|
var data = new FileInfo(TempDownload);
|
||||||
@@ -220,35 +204,28 @@ namespace VRCX
|
|||||||
logger.Error("Downloaded file size does not match expected size");
|
logger.Error("Downloaded file size does not match expected size");
|
||||||
throw new Exception("Downloaded file size does not match expected size");
|
throw new Exception("Downloaded file size does not match expected size");
|
||||||
}
|
}
|
||||||
if (File.Exists(HashLocation))
|
|
||||||
|
if (string.IsNullOrEmpty(hashString))
|
||||||
|
{
|
||||||
|
logger.Error("Hash string is empty, skipping hash check");
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
logger.Info("Checking hash");
|
logger.Info("Checking hash");
|
||||||
var lines = await File.ReadAllLinesAsync(HashLocation, _cancellationToken);
|
|
||||||
var hashDict = new Dictionary<string, string>();
|
|
||||||
foreach (var line in lines)
|
|
||||||
{
|
|
||||||
var split = line.Split(' ');
|
|
||||||
if (split.Length == 3)
|
|
||||||
hashDict[split[2]] = split[0];
|
|
||||||
}
|
|
||||||
using (var sha256 = SHA256.Create())
|
using (var sha256 = SHA256.Create())
|
||||||
await using (var stream = File.OpenRead(TempDownload))
|
await using (var stream = File.OpenRead(TempDownload))
|
||||||
{
|
{
|
||||||
var hashBytes = await sha256.ComputeHashAsync(stream, _cancellationToken);
|
var fileHashBytes = await sha256.ComputeHashAsync(stream, _cancellationToken);
|
||||||
var hashString = BitConverter.ToString(hashBytes).Replace("-", "");
|
var fileHashString = Convert.ToHexString(fileHashBytes);
|
||||||
if (!hashDict.TryGetValue(fileName, out var expectedHash))
|
if (!string.IsNullOrEmpty(fileHashString) &&
|
||||||
|
!hashString.Equals(fileHashString, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
logger.Error("Hash check failed, file not found in hash file");
|
logger.Error($"Hash check failed file:{fileHashString} web:{hashString}");
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(expectedHash) &&
|
|
||||||
!hashString.Equals(expectedHash, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
logger.Error($"Hash check failed file:{hashString} web:{expectedHash}");
|
|
||||||
throw new Exception("Hash check failed");
|
throw new Exception("Hash check failed");
|
||||||
// can't delete file yet because it's in use
|
// can't delete file yet because it's in use
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File.Delete(HashLocation);
|
|
||||||
logger.Info("Hash check passed");
|
logger.Info("Hash check passed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<el-progress
|
<el-progress
|
||||||
type="circle"
|
type="circle"
|
||||||
width="50"
|
width="50"
|
||||||
stroke-width="3"
|
:stroke-width="3"
|
||||||
:percentage="updateProgress"
|
:percentage="updateProgress"
|
||||||
:format="updateProgressText"></el-progress>
|
:format="updateProgressText"></el-progress>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+26
-47
@@ -3,7 +3,6 @@ import { computed, reactive } from 'vue';
|
|||||||
import * as workerTimers from 'worker-timers';
|
import * as workerTimers from 'worker-timers';
|
||||||
import { $app } from '../app';
|
import { $app } from '../app';
|
||||||
import configRepository from '../service/config';
|
import configRepository from '../service/config';
|
||||||
import { watchState } from '../service/watchState';
|
|
||||||
import { branches } from '../shared/constants';
|
import { branches } from '../shared/constants';
|
||||||
import { changeLogRemoveLinks } from '../shared/utils';
|
import { changeLogRemoveLinks } from '../shared/utils';
|
||||||
import { useUiStore } from './ui';
|
import { useUiStore } from './ui';
|
||||||
@@ -225,8 +224,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
|||||||
state.VRCXUpdateDialog.updatePendingIsLatest = true;
|
state.VRCXUpdateDialog.updatePendingIsLatest = true;
|
||||||
} else if (releaseName > currentVersion.value) {
|
} else if (releaseName > currentVersion.value) {
|
||||||
let downloadUrl = '';
|
let downloadUrl = '';
|
||||||
let downloadName = '';
|
let hashString = '';
|
||||||
let hashUrl = '';
|
|
||||||
let size = 0;
|
let size = 0;
|
||||||
for (const asset of json.assets) {
|
for (const asset of json.assets) {
|
||||||
if (asset.state !== 'uploaded') {
|
if (asset.state !== 'uploaded') {
|
||||||
@@ -239,7 +237,12 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
|||||||
'application/x-msdos-program')
|
'application/x-msdos-program')
|
||||||
) {
|
) {
|
||||||
downloadUrl = asset.browser_download_url;
|
downloadUrl = asset.browser_download_url;
|
||||||
downloadName = asset.name;
|
if (
|
||||||
|
asset.digest &&
|
||||||
|
asset.digest.startsWith('sha256:')
|
||||||
|
) {
|
||||||
|
hashString = asset.digest.replace('sha256:', '');
|
||||||
|
}
|
||||||
size = asset.size;
|
size = asset.size;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -248,34 +251,29 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
|||||||
asset.content_type === 'application/octet-stream'
|
asset.content_type === 'application/octet-stream'
|
||||||
) {
|
) {
|
||||||
downloadUrl = asset.browser_download_url;
|
downloadUrl = asset.browser_download_url;
|
||||||
downloadName = asset.name;
|
if (
|
||||||
|
asset.digest &&
|
||||||
|
asset.digest.startsWith('sha256:')
|
||||||
|
) {
|
||||||
|
hashString = asset.digest.replace('sha256:', '');
|
||||||
|
}
|
||||||
size = asset.size;
|
size = asset.size;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
asset.name === 'SHA256SUMS.txt' &&
|
|
||||||
asset.content_type === 'text/plain'
|
|
||||||
) {
|
|
||||||
hashUrl = asset.browser_download_url;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!downloadUrl) {
|
if (!downloadUrl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.pendingVRCXUpdate = true;
|
state.pendingVRCXUpdate = true;
|
||||||
uiStore.notifyMenu('settings');
|
uiStore.notifyMenu('settings');
|
||||||
const type = 'Auto';
|
|
||||||
if (state.autoUpdateVRCX === 'Notify') {
|
if (state.autoUpdateVRCX === 'Notify') {
|
||||||
// this.showVRCXUpdateDialog();
|
// this.showVRCXUpdateDialog();
|
||||||
} else if (state.autoUpdateVRCX === 'Auto Download') {
|
} else if (state.autoUpdateVRCX === 'Auto Download') {
|
||||||
await downloadVRCXUpdate(
|
await downloadVRCXUpdate(
|
||||||
downloadUrl,
|
downloadUrl,
|
||||||
downloadName,
|
hashString,
|
||||||
hashUrl,
|
|
||||||
size,
|
size,
|
||||||
releaseName,
|
releaseName
|
||||||
type
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,11 +342,9 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
|||||||
}
|
}
|
||||||
async function downloadVRCXUpdate(
|
async function downloadVRCXUpdate(
|
||||||
downloadUrl,
|
downloadUrl,
|
||||||
downloadName,
|
hashString,
|
||||||
hashUrl,
|
|
||||||
size,
|
size,
|
||||||
releaseName,
|
releaseName
|
||||||
type
|
|
||||||
) {
|
) {
|
||||||
if (state.updateInProgress) {
|
if (state.updateInProgress) {
|
||||||
return;
|
return;
|
||||||
@@ -356,12 +352,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
|||||||
try {
|
try {
|
||||||
state.updateInProgress = true;
|
state.updateInProgress = true;
|
||||||
await downloadFileProgress();
|
await downloadFileProgress();
|
||||||
await AppApi.DownloadUpdate(
|
await AppApi.DownloadUpdate(downloadUrl, hashString, size);
|
||||||
downloadUrl,
|
|
||||||
downloadName,
|
|
||||||
hashUrl,
|
|
||||||
size
|
|
||||||
);
|
|
||||||
state.pendingVRCXInstall = releaseName;
|
state.pendingVRCXInstall = releaseName;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -386,8 +377,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let downloadUrl = '';
|
let downloadUrl = '';
|
||||||
let downloadName = '';
|
let hashString = '';
|
||||||
let hashUrl = '';
|
|
||||||
let size = 0;
|
let size = 0;
|
||||||
for (const asset of release.assets) {
|
for (const asset of release.assets) {
|
||||||
if (asset.state !== 'uploaded') {
|
if (asset.state !== 'uploaded') {
|
||||||
@@ -399,7 +389,9 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
|||||||
asset.content_type === 'application/x-msdos-program')
|
asset.content_type === 'application/x-msdos-program')
|
||||||
) {
|
) {
|
||||||
downloadUrl = asset.browser_download_url;
|
downloadUrl = asset.browser_download_url;
|
||||||
downloadName = asset.name;
|
if (asset.digest && asset.digest.startsWith('sha256:')) {
|
||||||
|
hashString = asset.digest.replace('sha256:', '');
|
||||||
|
}
|
||||||
size = asset.size;
|
size = asset.size;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -408,31 +400,18 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
|||||||
asset.content_type === 'application/octet-stream'
|
asset.content_type === 'application/octet-stream'
|
||||||
) {
|
) {
|
||||||
downloadUrl = asset.browser_download_url;
|
downloadUrl = asset.browser_download_url;
|
||||||
downloadName = asset.name;
|
if (asset.digest && asset.digest.startsWith('sha256:')) {
|
||||||
|
hashString = asset.digest.replace('sha256:', '');
|
||||||
|
}
|
||||||
size = asset.size;
|
size = asset.size;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
asset.name === 'SHA256SUMS.txt' &&
|
|
||||||
asset.content_type === 'text/plain'
|
|
||||||
) {
|
|
||||||
hashUrl = asset.browser_download_url;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!downloadUrl) {
|
if (!downloadUrl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const releaseName = release.name;
|
const releaseName = release.name;
|
||||||
const type = 'Manual';
|
downloadVRCXUpdate(downloadUrl, hashString, size, releaseName);
|
||||||
downloadVRCXUpdate(
|
|
||||||
downloadUrl,
|
|
||||||
downloadName,
|
|
||||||
hashUrl,
|
|
||||||
size,
|
|
||||||
releaseName,
|
|
||||||
type
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-2
@@ -313,8 +313,7 @@ declare global {
|
|||||||
// Update
|
// Update
|
||||||
DownloadUpdate(
|
DownloadUpdate(
|
||||||
fileUrl: string,
|
fileUrl: string,
|
||||||
fileName: string,
|
hashString: string,
|
||||||
hashUrl: string,
|
|
||||||
downloadSize: number
|
downloadSize: number
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
CancelUpdate(): Promise<void>;
|
CancelUpdate(): Promise<void>;
|
||||||
|
|||||||
Reference in New Issue
Block a user