Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Download MSIX installer during manifest validation (if necessary) #2587

Merged
merged 3 commits into from
Oct 11, 2022
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
105 changes: 90 additions & 15 deletions src/AppInstallerCommonCore/Manifest/MsixManifestValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the MIT License.
#include "pch.h"
#include "AppInstallerLogging.h"
#include "AppInstallerRuntime.h"
#include "AppInstallerDownloader.h"
#include "winget/MsixManifestValidation.h"

namespace AppInstaller::Manifest
Expand All @@ -12,7 +14,7 @@ namespace AppInstaller::Manifest
{
std::vector<ValidationError> errors;
Msix::PackageVersion packageVersion(manifest.Version);
auto msixInfo = GetMsixInfo(installer.Url, errors);
auto msixInfo = GetMsixInfo(installer.Url);
if (msixInfo)
{
ValidateMsixManifestSignatureHash(msixInfo, installer.SignatureSha256, errors);
Expand All @@ -26,37 +28,110 @@ namespace AppInstaller::Manifest
ValidateMsixManifestMinOSVersion(msixManifest.GetMinimumOSVersionForSupportedPlatforms(), installerMinOSVersion, installer.Url, errors);
}
}
else
{
errors.emplace_back(ManifestError::InstallerFailedToProcess, "InstallerUrl", installer.Url);
}

return errors;
}

std::shared_ptr<Msix::MsixInfo> MsixManifestValidation::GetMsixInfo(
std::string installerUrl,
std::vector<ValidationError> &errors)
std::optional<std::filesystem::path> MsixManifestValidation::DownloadInstaller(std::string installerUrl, int retryCount)
{
while (retryCount-- > 0)
{
try
{
AICLI_LOG(Core, Info, << "Start downloading installer");
auto tempFile = Runtime::GetNewTempFilePath();
ProgressCallback callback;
Utility::Download(installerUrl, tempFile, Utility::DownloadType::Installer, callback);
return tempFile;
}
catch (...)
{
AICLI_LOG(Core, Error, << "Downloading installer failed. Remaining attempts: " << retryCount);
}
}

return std::nullopt;
}

std::shared_ptr<Msix::MsixInfo> MsixManifestValidation::GetMsixInfoFromUrl(std::string installerUrl)
{
std::shared_ptr<Msix::MsixInfo> msixInfo;
try
{
// Cache Msix info for new installer url
auto findMsixInfo = m_msixInfoCache.find(installerUrl);
if (findMsixInfo == m_msixInfoCache.end())
AICLI_LOG(Core, Info, << "Fetching Msix info from installer url");
return std::make_shared<Msix::MsixInfo>(installerUrl);
}
catch (...)
{
AICLI_LOG(Core, Error, << "Error fetching Msix info from the installer url.");
return nullptr;
}
}

std::shared_ptr<Msix::MsixInfo> MsixManifestValidation::GetMsixInfoFromLocalPath(std::string installerUrl)
{
int maxRetry = 3;
std::shared_ptr<Msix::MsixInfo> msixInfo;
auto installerPath = DownloadInstaller(installerUrl, maxRetry);
if (installerPath.has_value())
{
try
{
AICLI_LOG(Core, Info, << "Fetching Msix info from installer local path");
msixInfo = std::make_shared<Msix::MsixInfo>(installerPath.value());
}
catch (...)
{
AICLI_LOG(Core, Error, << "Error fetching Msix info from the installer local path.");
}

AICLI_LOG(Core, Info, << "Removing downloaded installer");
if (!std::filesystem::remove(installerPath.value()))
{
AICLI_LOG(Core, Warning, << "Failed to remove downloaded installer");
}
}
else
{
AICLI_LOG(Core, Error, << "Failed to download installer.");
}

return msixInfo;
}

std::shared_ptr<Msix::MsixInfo> MsixManifestValidation::GetMsixInfo(std::string installerUrl)
{
std::shared_ptr<Msix::MsixInfo> msixInfo;
// Cache Msix info for new installer url
auto findMsixInfo = m_msixInfoCache.find(installerUrl);
if (findMsixInfo == m_msixInfoCache.end())
{
msixInfo = GetMsixInfoFromUrl(installerUrl);
if (!msixInfo)
{
AICLI_LOG(Core, Warning, << "Failed to get Msix info directly from the installer url. "
<< "Downloading installer instead.");
msixInfo = GetMsixInfoFromLocalPath(installerUrl);
}

if (msixInfo)
{
msixInfo = std::make_shared<Msix::MsixInfo>(installerUrl);
m_msixInfoCache.insert({ installerUrl, msixInfo });
}
else
{
msixInfo = findMsixInfo->second;
AICLI_LOG(Core, Error, << "Msix info could not be obtained.");
}

return msixInfo;
}
catch (...)
else
{
errors.emplace_back(ManifestError::InstallerFailedToProcess, "InstallerUrl", installerUrl);
msixInfo = findMsixInfo->second;
}

return nullptr;
return msixInfo;
}

std::optional<Msix::OSVersion> MsixManifestValidation::GetManifestInstallerMinOSVersion(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,21 @@ namespace AppInstaller::Manifest
std::map<std::string, std::shared_ptr<Msix::MsixInfo>> m_msixInfoCache;
ValidationError::Level m_validationErrorLevel;

// Get Msix info from installer url, or load it from cache. Return null
// pointer if failed to process installer url.
std::shared_ptr<Msix::MsixInfo> GetMsixInfo(
std::string installerUrl,
std::vector<ValidationError>& errors);
// Get Msix info from url/local path, or load it from cache.
// Return null pointer if operation failed.
std::shared_ptr<Msix::MsixInfo> GetMsixInfo(std::string installerUrl);

// Get Msix info from installer url.
// Return null pointer if operation failed.
std::shared_ptr<Msix::MsixInfo> GetMsixInfoFromUrl(std::string installerUrl);

// Download and get msix info from installer local path.
// Return null pointer if operation failed.
std::shared_ptr<Msix::MsixInfo> GetMsixInfoFromLocalPath(std::string installerUrl);

// Download the installer.
// If the download was successful, return the destination path.
std::optional<std::filesystem::path> DownloadInstaller(std::string installerUrl, int retryCount);

// Get manifest installer minimum OS version or nullopt if failed to
// parse input.
Expand Down