From ff27bdf874b5a71ac79ee48a6e4b5b9fea69f776 Mon Sep 17 00:00:00 2001 From: Caelan Sayler Date: Tue, 11 Jun 2024 09:14:50 +0100 Subject: [PATCH] Move file escape to utility and add test for filename web-uri downloading --- src/Velopack/Internal/Utility.cs | 22 +++++++++++++ src/Velopack/UpdateManager.cs | 26 ++------------- test/Velopack.Tests/UpdateManagerTests.cs | 40 +++++++++++++++++++++++ 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/Velopack/Internal/Utility.cs b/src/Velopack/Internal/Utility.cs index 0077867e..626df82e 100644 --- a/src/Velopack/Internal/Utility.cs +++ b/src/Velopack/Internal/Utility.cs @@ -264,6 +264,28 @@ namespace Velopack })); } + /// + /// Escapes file name such that the file name is safe for writing to disk in the packages folder + /// + public static string GetSafeFilename(string fileName) + { + string safeFileName = Path.GetFileName(fileName); + char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); + + if (safeFileName.IndexOfAny(invalidFileNameChars) != -1) { + StringBuilder safeName = new(); + foreach (char ch in safeFileName) { + if (Array.IndexOf(invalidFileNameChars, ch) == -1) + safeName.Append(ch); + else + safeName.Append('_'); + } + safeFileName = safeName.ToString(); + } + + return safeFileName; + } + public static string GetDefaultTempBaseDirectory() { string tempDir; diff --git a/src/Velopack/UpdateManager.cs b/src/Velopack/UpdateManager.cs index e85afbc2..c2e55cc9 100644 --- a/src/Velopack/UpdateManager.cs +++ b/src/Velopack/UpdateManager.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -232,7 +231,7 @@ namespace Velopack var appTempDir = Locator.AppTempDir!; var appPackageDir = Locator.PackagesDir!; - var completeFile = Path.Combine(appPackageDir, GetSafeFilename(targetRelease.FileName)); + var completeFile = Path.Combine(appPackageDir, Utility.GetSafeFilename(targetRelease.FileName)); var incompleteFile = completeFile + ".partial"; try { @@ -261,7 +260,7 @@ namespace Velopack $"Only full update will be available."); } else { using var _1 = Utility.GetTempDirectory(out var deltaStagingDir, appTempDir); - string basePackagePath = Path.Combine(appPackageDir, GetSafeFilename(updates.BaseRelease.FileName)); + string basePackagePath = Path.Combine(appPackageDir, Utility.GetSafeFilename(updates.BaseRelease.FileName)); if (!File.Exists(basePackagePath)) throw new Exception($"Unable to find base package {basePackagePath} for delta update."); EasyZip.ExtractZipToDirectory(Log, basePackagePath, deltaStagingDir); @@ -319,27 +318,6 @@ namespace Velopack CleanPackagesExcept(completeFile); } - - - // Ensures that the file name is safe for writing to disk without escaping the packages folder - static string GetSafeFilename(string fileName) - { - string safeFileName = Path.GetFileName(fileName); - char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); - - if (safeFileName.IndexOfAny(invalidFileNameChars) != -1 ) { - StringBuilder safeName = new(); - foreach(char ch in safeFileName) { - if (Array.IndexOf(invalidFileNameChars, ch) == -1) - safeName.Append(ch); - else - safeName.Append('_'); - } - safeFileName = safeName.ToString(); - } - - return safeFileName; - } } /// diff --git a/test/Velopack.Tests/UpdateManagerTests.cs b/test/Velopack.Tests/UpdateManagerTests.cs index e4d39a11..179dda62 100644 --- a/test/Velopack.Tests/UpdateManagerTests.cs +++ b/test/Velopack.Tests/UpdateManagerTests.cs @@ -100,6 +100,46 @@ public class UpdateManagerTests return new FakeDownloader() { MockedResponseBytes = Encoding.UTF8.GetBytes(json) }; } + [Fact] + public void CanDownloadFilesAsUrl() + { + var fixture = PathHelper.GetFixture("AvaloniaCrossPlat-1.0.11-win-full.nupkg"); + + using var logger = _output.BuildLoggerFor(); + using var _1 = Utility.GetTempDirectory(out var tempPath); + var dl = new FakeDownloader() { + MockedResponseBytes = Encoding.UTF8.GetBytes(SimpleJson.SerializeObject( + new VelopackAssetFeed { + Assets = new VelopackAsset[] { + new VelopackAsset() { + PackageId = "AvaloniaCrossPlat", + Version = new SemanticVersion(1, 0, 11), + Type = VelopackAssetType.Full, + FileName = $"https://mysite.com/releases/AvaloniaCrossPlat$-1.1.0.nupkg", + SHA1 = Utility.CalculateFileSHA1(fixture), + Size = new FileInfo(fixture).Length, + } } + })) + }; + var source = new SimpleWebSource("http://any.com", dl); + var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); + var um = new UpdateManager(source, null, logger, locator); + var info = um.CheckForUpdates(); + Assert.NotNull(info); + Assert.True(new SemanticVersion(1, 0, 11) == info.TargetFullRelease.Version); + Assert.Equal(0, info.DeltasToTarget.Count()); + Assert.False(info.IsDowngrade); + Assert.StartsWith($"http://any.com/releases.{VelopackRuntimeInfo.SystemOs.GetOsShortName()}.json?", dl.LastUrl); + + dl.MockedResponseBytes = File.ReadAllBytes(fixture); + dl.WriteMockLocalFile = true; + um.DownloadUpdates(info); + + Assert.True(File.Exists(Path.Combine(tempPath, "AvaloniaCrossPlat$-1.1.0.nupkg"))); + Assert.Equal(Path.Combine(tempPath, "AvaloniaCrossPlat$-1.1.0.nupkg.partial"), dl.LastLocalFile); + Assert.Equal("https://mysite.com/releases/AvaloniaCrossPlat$-1.1.0.nupkg", dl.LastUrl); + } + [Fact] public void CheckFromLocal() {