From 9d30e9f89595c612700d2d959232b1f8b633873e Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 11 Oct 2022 14:25:14 -0700 Subject: [PATCH] Download MSIX installer during manifest validation (if necessary) (#2587) --- .../Manifest/MsixManifestValidation.cpp | 105 +++++++++++++++--- .../Public/winget/MsixManifestValidation.h | 20 +++- 2 files changed, 105 insertions(+), 20 deletions(-) diff --git a/src/AppInstallerCommonCore/Manifest/MsixManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/MsixManifestValidation.cpp index 9e2ecf990b..69c3963bfb 100644 --- a/src/AppInstallerCommonCore/Manifest/MsixManifestValidation.cpp +++ b/src/AppInstallerCommonCore/Manifest/MsixManifestValidation.cpp @@ -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 @@ -12,7 +14,7 @@ namespace AppInstaller::Manifest { std::vector errors; Msix::PackageVersion packageVersion(manifest.Version); - auto msixInfo = GetMsixInfo(installer.Url, errors); + auto msixInfo = GetMsixInfo(installer.Url); if (msixInfo) { ValidateMsixManifestSignatureHash(msixInfo, installer.SignatureSha256, errors); @@ -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 MsixManifestValidation::GetMsixInfo( - std::string installerUrl, - std::vector &errors) + std::optional 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 MsixManifestValidation::GetMsixInfoFromUrl(std::string installerUrl) { - std::shared_ptr 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(installerUrl); + } + catch (...) + { + AICLI_LOG(Core, Error, << "Error fetching Msix info from the installer url."); + return nullptr; + } + } + + std::shared_ptr MsixManifestValidation::GetMsixInfoFromLocalPath(std::string installerUrl) + { + int maxRetry = 3; + std::shared_ptr 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(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 MsixManifestValidation::GetMsixInfo(std::string installerUrl) + { + std::shared_ptr 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(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 MsixManifestValidation::GetManifestInstallerMinOSVersion( diff --git a/src/AppInstallerCommonCore/Public/winget/MsixManifestValidation.h b/src/AppInstallerCommonCore/Public/winget/MsixManifestValidation.h index acd7bdfb51..b46fee63d8 100644 --- a/src/AppInstallerCommonCore/Public/winget/MsixManifestValidation.h +++ b/src/AppInstallerCommonCore/Public/winget/MsixManifestValidation.h @@ -20,11 +20,21 @@ namespace AppInstaller::Manifest std::map> 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 GetMsixInfo( - std::string installerUrl, - std::vector& errors); + // Get Msix info from url/local path, or load it from cache. + // Return null pointer if operation failed. + std::shared_ptr GetMsixInfo(std::string installerUrl); + + // Get Msix info from installer url. + // Return null pointer if operation failed. + std::shared_ptr GetMsixInfoFromUrl(std::string installerUrl); + + // Download and get msix info from installer local path. + // Return null pointer if operation failed. + std::shared_ptr GetMsixInfoFromLocalPath(std::string installerUrl); + + // Download the installer. + // If the download was successful, return the destination path. + std::optional DownloadInstaller(std::string installerUrl, int retryCount); // Get manifest installer minimum OS version or nullopt if failed to // parse input.