diff --git a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp
index f01ce591ce..7b52aac36e 100644
--- a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp
+++ b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp
@@ -81,6 +81,9 @@ namespace AppInstaller::CLI::Workflow
filename += installerExtension;
+ // Make file name suitable for file system path
+ filename = Utility::ConvertToUTF16(Utility::MakeSuitablePathPart(filename.u8string()));
+
return filename;
}
diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
index 285c6c3178..490582234b 100644
--- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
+++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
@@ -264,6 +264,9 @@
true
+
+ true
+
true
diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters
index 4add709da6..c63d10d997 100644
--- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters
+++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters
@@ -525,6 +525,9 @@
TestData
+
+ TestData
+
TestData
diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_InvalidFileCharacterUrl.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_InvalidFileCharacterUrl.yaml
new file mode 100644
index 0000000000..028e4c9784
--- /dev/null
+++ b/src/AppInstallerCLITests/TestData/InstallFlowTest_InvalidFileCharacterUrl.yaml
@@ -0,0 +1,18 @@
+Id: AppInstallerCliTest.InvalidFileCharacterUrlTest
+Version: 1.0.0.0
+Name: AppInstaller Test Exe Installer
+Publisher: Microsoft Corporation
+AppMoniker: AICLITestExe
+License: Test
+ProductCode: AppInstallerCliTest.TestExeInstaller
+Installers:
+ - Arch: x64
+ Url: https://EncodedUrlTest/te*st.exe
+ InstallerType: exe
+ Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B
+ Switches:
+ Custom: /invalidFileCharacterUrl
+ SilentWithProgress: /silentwithprogress
+ Silent: /silence
+ Update: /update
+ManifestVersion: 0.1.0
diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp
index 9536d2165f..b8638ad5bc 100644
--- a/src/AppInstallerCLITests/WorkFlow.cpp
+++ b/src/AppInstallerCLITests/WorkFlow.cpp
@@ -930,6 +930,38 @@ TEST_CASE("InstallFlow_RenameFromEncodedUrl", "[InstallFlow][workflow]")
REQUIRE(installResultStr.find("/encodedUrl") != std::string::npos);
}
+TEST_CASE("InstallFlow_RenameFromInvalidFileCharacterUrl", "[InstallFlow][workflow]")
+{
+ TestCommon::TempFile installResultPath("TestExeInstalled.txt");
+
+ std::ostringstream installOutput;
+ TestContext context{ installOutput, std::cin };
+ auto previousThreadGlobals = context.SetForCurrentThread();
+ OverrideForCheckExistingInstaller(context);
+ context.Override({ DownloadInstallerFile, [](TestContext& context)
+ {
+ context.Add({ {}, {} });
+ auto installerPath = std::filesystem::temp_directory_path();
+ installerPath /= "InvalidFileCharacterUrlTest.exe";
+ std::filesystem::copy(TestDataFile("AppInstallerTestExeInstaller.exe"), installerPath, std::filesystem::copy_options::overwrite_existing);
+ context.Add(installerPath);
+ } });
+ OverrideForUpdateInstallerMotw(context);
+ context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_InvalidFileCharacterUrl.yaml").GetPath().u8string());
+
+ InstallCommand install({});
+ install.Execute(context);
+ INFO(installOutput.str());
+
+ // Verify Installer is called and parameters are passed in.
+ REQUIRE(std::filesystem::exists(installResultPath.GetPath()));
+ std::ifstream installResultFile(installResultPath.GetPath());
+ REQUIRE(installResultFile.is_open());
+ std::string installResultStr;
+ std::getline(installResultFile, installResultStr);
+ REQUIRE(installResultStr.find("/invalidFileCharacterUrl") != std::string::npos);
+}
+
TEST_CASE("InstallFlowNonZeroExitCode", "[InstallFlow][workflow]")
{
TestCommon::TempFile installResultPath("TestExeInstalled.txt");
diff --git a/src/AppInstallerCommonCore/Downloader.cpp b/src/AppInstallerCommonCore/Downloader.cpp
index d07609a3cc..be43976703 100644
--- a/src/AppInstallerCommonCore/Downloader.cpp
+++ b/src/AppInstallerCommonCore/Downloader.cpp
@@ -32,17 +32,19 @@ namespace AppInstaller::Utility
AICLI_LOG(Core, Info, << "WinINet downloading from url: " << url);
- wil::unique_hinternet session(InternetOpenA(
- Runtime::GetDefaultUserAgent().get().c_str(),
+ auto agentWide = Utility::ConvertToUTF16(Runtime::GetDefaultUserAgent().get());
+ wil::unique_hinternet session(InternetOpen(
+ agentWide.c_str(),
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
0));
THROW_LAST_ERROR_IF_NULL_MSG(session, "InternetOpen() failed.");
- wil::unique_hinternet urlFile(InternetOpenUrlA(
+ auto urlWide = Utility::ConvertToUTF16(url);
+ wil::unique_hinternet urlFile(InternetOpenUrl(
session.get(),
- url.c_str(),
+ urlWide.c_str(),
NULL,
0,
INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS, // This allows http->https redirection
@@ -53,7 +55,7 @@ namespace AppInstaller::Utility
DWORD requestStatus = 0;
DWORD cbRequestStatus = sizeof(requestStatus);
- THROW_LAST_ERROR_IF_MSG(!HttpQueryInfoA(urlFile.get(),
+ THROW_LAST_ERROR_IF_MSG(!HttpQueryInfo(urlFile.get(),
HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
&requestStatus,
&cbRequestStatus,
@@ -71,7 +73,7 @@ namespace AppInstaller::Utility
LONGLONG contentLength = 0;
DWORD cbContentLength = sizeof(contentLength);
- HttpQueryInfoA(
+ HttpQueryInfo(
urlFile.get(),
HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER64,
&contentLength,