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()
{