commit 9399b6a2c63d25190cff455700bc9f70c5afbeec parent cf6f8e58195bf033a1a5872f7b800866cd27a586 Author: JohnMcPMS <johnmcp@microsoft.com> Date: Thu, 29 Apr 2021 21:46:55 -0700 Implement Delivery Optimization (#909) ## Change This change adds code to leverage the Delivery Optimization (DO) service to download packages. Only packages are downloaded through DO at this time, as other things that we download do not fit in with the design of DO. Currently one must still opt-in to using DO via settings, which I used rather than an experimental feature because it makes sense to allow the control long term. Eventually, we will make DO the default value here. ## Settings Two settings are added: 1. Control over which downloader is used for packages (`.network.downloader`) 2. A timeout for controlling how long to wait before DO indicates any progress has been made (`.network.doProgressTimeoutInSeconds`) Diffstat:
21 files changed, 1176 insertions(+), 31 deletions(-)
diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt @@ -3,7 +3,7 @@ ACCESSDENIED addmanifest addstore admins -Alloc +alloc api appdata appinst @@ -21,6 +21,8 @@ argv ARRAYSIZE aspirational aspnet +authn +authz autocomplete auxdata azureedge @@ -30,7 +32,7 @@ Bitmask blog Blog boolalpha -BSTR +bstr bugfix BUILDNUMBER bytearray @@ -104,6 +106,7 @@ ensureandinsert ensurepathexists ENU enum +EOAC errorlevel errstr esrp @@ -166,6 +169,7 @@ icu IDisposable IDX IEnumerable +IFACEMETHOD ifdef ifndef ifstream @@ -503,7 +507,7 @@ wil WINAPI WINEVENT winget -WININET +wininet winmeta winres winrt diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt @@ -22,3 +22,4 @@ ^src/AppInstallerCLITests/TestData/InputNames.txt$ ^src/AppInstallerCLITests/TestData/InputPublishers.txt$ ^src/AppInstallerCLITests/TestData/NormalizationInitialIds.txt$ +^src/AppInstallerCommonCore/external/do.h$ diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt @@ -242,6 +242,7 @@ UChars uec uild uintptr +ul Uninitialize uninstallation uninstaller diff --git a/doc/Settings.md b/doc/Settings.md @@ -83,6 +83,22 @@ See [details on telemetry](../README.md#datatelemetry), and our [primary privacy If set to true, the `telemetry.disable` setting will prevent any event from being written by the program. +## Network + +The `network` settings influence how winget uses the network to retrieve packages and metadata. + +### Downloader + +The `downloader` setting controls which code is used when downloading packages. The default is `default`, which may be any of the options based on our determination. +`wininet` uses the [WinINet](https://docs.microsoft.com/en-us/windows/win32/wininet/about-wininet) APIs, while `do` uses the +[Delivery Optimization](https://support.microsoft.com/en-us/windows/delivery-optimization-in-windows-10-0656e53c-15f2-90de-a87a-a2172c94cf6d) service. + +```json + "network": { + "downloader": "do" + } +``` + ## Experimental Features To allow work to be done and distributed to early adopters for feedback, settings can be used to enable "experimental" features. diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json @@ -40,7 +40,8 @@ "description": "The scope of a package install", "type": "string", "enum": [ - "user", "machine" + "user", + "machine" ], "default": "user" } @@ -50,8 +51,8 @@ "description": "Install settings", "type": "object", "properties": { - "preferences": { "$ref": "#/definitions/InstallPrefReq"}, - "requirements": { "$ref": "#/definitions/InstallPrefReq"} + "preferences": { "$ref": "#/definitions/InstallPrefReq" }, + "requirements": { "$ref": "#/definitions/InstallPrefReq" } } }, "Telemetry": { @@ -65,6 +66,29 @@ } } }, + "Network": { + "description": "Network settings", + "type": "object", + "properties": { + "downloader": { + "description": "Control which download code is used for packages", + "type": "string", + "enum": [ + "default", + "wininet", + "do" + ], + "default": "default" + }, + "doProgressTimeoutInSeconds": { + "description": "Number of seconds to wait without progress before fallback", + "type": "integer", + "default": 20, + "minimum": 1, + "maximum": 600 + } + } + }, "Experimental": { "description": "Experimental Features", "type": "object", @@ -99,11 +123,6 @@ "type": "boolean", "default": false }, - "import": { - "description": "Enable the import command while it is in development", - "type": "boolean", - "default": false - }, "restSource": { "description": "Enable the rest source support while it is in development", "type": "boolean", @@ -121,25 +140,31 @@ }, { "properties": { - "source": { "$ref": "#/definitions/Source"} + "source": { "$ref": "#/definitions/Source" } + }, + "additionalItems": true + }, + { + "properties": { + "installBehavior": { "$ref": "#/definitions/InstallBehavior" } }, "additionalItems": true }, { "properties": { - "installBehavior": { "$ref": "#/definitions/InstallBehavior"} + "telemetry": { "$ref": "#/definitions/Telemetry" } }, "additionalItems": true }, { "properties": { - "telemetry": { "$ref": "#/definitions/Telemetry"} + "network": { "$ref": "#/definitions/Network" } }, "additionalItems": true }, { "properties": { - "experimentalFeatures": { "$ref": "#/definitions/Experimental"} + "experimentalFeatures": { "$ref": "#/definitions/Experimental" } }, "additionalItems": true } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -105,6 +105,9 @@ namespace AppInstaller::CLI::Workflow std::filesystem::path tempInstallerPath = Runtime::GetPathTo(Runtime::PathName::Temp); tempInstallerPath /= Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version); + // Use the SHA256 hash of the installer as the identifier for the download + std::string hashString = SHA256::ConvertToString(installer.Sha256); + AICLI_LOG(CLI, Info, << "Generated temp download path: " << tempInstallerPath); context.Reporter.Info() << "Downloading " << Execution::UrlEmphasis << installer.Url << std::endl; @@ -120,8 +123,10 @@ namespace AppInstaller::CLI::Workflow hash = context.Reporter.ExecuteWithProgress(std::bind(Utility::Download, installer.Url, tempInstallerPath, + Utility::DownloadType::Installer, std::placeholders::_1, - true)); + true, + hashString)); success = true; } diff --git a/src/AppInstallerCLITests/Downloader.cpp b/src/AppInstallerCLITests/Downloader.cpp @@ -16,7 +16,7 @@ TEST_CASE("DownloadValidFileAndVerifyHash", "[Downloader]") // Todo: point to files from our repo when the repo goes public ProgressCallback callback; - auto result = Download("https://raw.githubusercontent.com/microsoft/msix-packaging/master/LICENSE", tempFile.GetPath(), callback, true); + auto result = Download("https://raw.githubusercontent.com/microsoft/msix-packaging/master/LICENSE", tempFile.GetPath(), DownloadType::Manifest, callback, true); REQUIRE(result.has_value()); auto resultHash = result.value(); @@ -49,7 +49,7 @@ TEST_CASE("DownloadValidFileAndCancel", "[Downloader]") std::optional<std::vector<BYTE>> waitResult; std::thread waitThread([&] { - waitResult = Download("https://aka.ms/win32-x64-user-stable", tempFile.GetPath(), callback, true); + waitResult = Download("https://aka.ms/win32-x64-user-stable", tempFile.GetPath(), DownloadType::Installer, callback, true); }); callback.Cancel(); @@ -66,5 +66,5 @@ TEST_CASE("DownloadInvalidUrl", "[Downloader]") ProgressCallback callback; - REQUIRE_THROWS_HR(Download("blargle-flargle-fluff", tempFile.GetPath(), callback, true), WININET_E_UNRECOGNIZED_SCHEME); + REQUIRE_THROWS_HR(Download("blargle-flargle-fluff", tempFile.GetPath(), DownloadType::Installer, callback, true), WININET_E_UNRECOGNIZED_SCHEME); } diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -260,6 +260,7 @@ </Link> </ItemDefinitionGroup> <ItemGroup> + <ClInclude Include="DODownloader.h" /> <ClInclude Include="Public\winget\GroupPolicy.h" /> <ClInclude Include="HttpStream\HttpClientWrapper.h" /> <ClInclude Include="HttpStream\HttpLocalCache.h" /> @@ -307,6 +308,7 @@ <ClInclude Include="YamlWrapper.h" /> </ItemGroup> <ItemGroup> + <ClCompile Include="DODownloader.cpp" /> <ClCompile Include="GroupPolicy.cpp"> <ExcludedFromBuild Condition="'$(Configuration)'=='Fuzzing'">true</ExcludedFromBuild> </ClCompile> diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters @@ -165,6 +165,9 @@ <ClInclude Include="Public\winget\Resources.h"> <Filter>Public\winget</Filter> </ClInclude> + <ClInclude Include="DODownloader.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp"> @@ -278,6 +281,9 @@ <ClCompile Include="GroupPolicy.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="DODownloader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> diff --git a/src/AppInstallerCommonCore/DODownloader.cpp b/src/AppInstallerCommonCore/DODownloader.cpp @@ -0,0 +1,395 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "DODownloader.h" +#include "Public/AppInstallerLogging.h" +#include "Public/AppInstallerSHA256.h" +#include "Public/AppInstallerStrings.h" +#include "winget/UserSettings.h" + +// TODO: Get this from the Windows SDK when available +#include "external/do.h" + +namespace AppInstaller::Utility +{ + namespace DeliveryOptimization + { +#define DO_E_DOWNLOAD_NO_PROGRESS HRESULT(0x80D02002L) // Download of a file saw no progress within the defined period + + // Represents a download work item for Delivery Optimization. + struct Download + { + Download(IDOManager* manager) + { + THROW_IF_FAILED(manager->CreateDownload(&m_download)); + + // Cloaking - sets the authentication information that will be used to make calls on the DO interface proxy. + // This will make sure DO server impersonates the correct client identity. + THROW_IF_FAILED(CoSetProxyBlanket( + m_download.get(), + RPC_C_AUTHN_DEFAULT, + RPC_C_AUTHZ_DEFAULT, + COLE_DEFAULT_PRINCIPAL, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_DEFAULT)); + } + + ~Download() + { + DO_DOWNLOAD_STATUS downloadStatus; + if (SUCCEEDED_LOG(m_download->GetStatus(&downloadStatus))) + { + if (downloadStatus.State == DODownloadState_Transferred) + { + // Calling IDODownload::Finalize() to inform DO that the DO job can be cleaned up. + // Otherwise, the resources associated with the job can be kept for a number of days + // until expiration set by DO. + (void)LOG_IF_FAILED(m_download->Finalize()); + } + else if (downloadStatus.State != DODownloadState_Finalized) + { + // For any other state, abort the download since it's no longer in use. + // This will allow DO to clean up the cache for the associated content ID. + (void)LOG_IF_FAILED(m_download->Abort()); + } + } + } + + void SetProperty(DODownloadProperty prop, const std::wstring& value) + { + wil::unique_variant var; + var.bstrVal = ::SysAllocString(value.c_str()); + THROW_IF_NULL_ALLOC(var.bstrVal); + var.vt = VT_BSTR; + THROW_IF_FAILED(m_download->SetProperty(prop, &var)); + } + + void SetProperty(DODownloadProperty prop, std::string_view value) + { + SetProperty(prop, Utility::ConvertToUTF16(value)); + } + + void SetProperty(DODownloadProperty prop, uint32_t value) + { + wil::unique_variant var; + var.ulVal = value; + var.vt = VT_UI4; + THROW_IF_FAILED(m_download->SetProperty(prop, &var)); + } + + void SetProperty(DODownloadProperty prop, bool value) + { + wil::unique_variant var; + var.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE; + var.vt = VT_BOOL; + THROW_IF_FAILED(m_download->SetProperty(prop, &var)); + } + + template<typename T> + void SetUnknownProperty(DODownloadProperty prop, T&& value) + { + wil::unique_variant var; + var.punkVal = nullptr; + var.vt = VT_UNKNOWN; + if (value) + { + THROW_IF_FAILED(value->QueryInterface(IID_PPV_ARGS(&var.punkVal))); + } + THROW_IF_FAILED(m_download->SetProperty(prop, &var)); + } + + void Uri(std::string_view uri) + { + SetProperty(DODownloadProperty_Uri, uri); + } + + void ContentId(std::string_view contentId) + { + SetProperty(DODownloadProperty_ContentId, contentId); + } + + void DisplayName(std::string_view displayName) + { + SetProperty(DODownloadProperty_DisplayName, displayName); + } + + void LocalPath(const std::filesystem::path& localPath) + { + SetProperty(DODownloadProperty_LocalPath, localPath.wstring()); + } + + void CorrelationVector(std::string_view correlationVector) + { + SetProperty(DODownloadProperty_CorrelationVector, correlationVector); + } + + void NoProgressTimeoutSeconds(uint32_t noProgressTimeoutSeconds) + { + SetProperty(DODownloadProperty_NoProgressTimeoutSeconds, noProgressTimeoutSeconds); + } + + void ForegroundPriority(bool foregroundPriority) + { + SetProperty(DODownloadProperty_ForegroundPriority, foregroundPriority); + } + + void BlockingMode(bool blockingMode) + { + SetProperty(DODownloadProperty_BlockingMode, blockingMode); + } + + void CallbackInterface(IDODownloadStatusCallback* callbackInterface) + { + SetUnknownProperty(DODownloadProperty_CallbackInterface, callbackInterface); + } + + void StreamInterface(IStream* streamInterface) + { + SetUnknownProperty(DODownloadProperty_StreamInterface, streamInterface); + } + + // Properties that may be interesting for future use: + // https://docs.microsoft.com/en-us/windows/win32/delivery_optimization/deliveryoptimizationdownloadtypes/ne-deliveryoptimizationdownloadtypes-dodownloadproperty + // - DODownloadProperty_CostPolicy :: Allow user to specify how to behave on metered networks + + void Start() + { + DO_DOWNLOAD_RANGES_INFO emptyRanges{}; + emptyRanges.RangeCount = 0; + THROW_IF_FAILED(m_download->Start(&emptyRanges)); + } + + // Returns true if Abort was successful; false if not. + bool Cancel() + { + return SUCCEEDED_LOG(m_download->Abort()); + } + + void Finalize() + { + THROW_IF_FAILED(m_download->Finalize()); + } + + DO_DOWNLOAD_STATUS Status() + { + DO_DOWNLOAD_STATUS result{}; + THROW_IF_FAILED(m_download->GetStatus(&result)); + return result; + } + + private: + wil::com_ptr<IDODownload> m_download; + }; + + // The top level Delivery Optimization manager object. + struct Manager + { + Manager() + { + THROW_IF_FAILED(CoCreateInstance( + __uuidof(::DeliveryOptimization), + nullptr, + CLSCTX_LOCAL_SERVER, + IID_PPV_ARGS(&m_manager))); + } + + Download CreateDownload() + { + return { m_manager.get() }; + } + + private: + wil::com_ptr<IDOManager> m_manager; + }; + + // Status callback handler + class DODownloadStatusCallback : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IDODownloadStatusCallback> + { + public: + DODownloadStatusCallback(IProgressCallback& progress) : + m_progress(progress) + { + } + + IFACEMETHOD(OnStatusChange)(IDODownload*, DO_DOWNLOAD_STATUS* status) + { + { + std::lock_guard<std::mutex> guard(m_statusMutex); + m_currentStatus = *status; + } + m_statusCV.notify_all(); + return S_OK; + } + + static HRESULT Create( + IProgressCallback& progress, + DODownloadStatusCallback** result) + { + Microsoft::WRL::ComPtr<DODownloadStatusCallback> localResult = Microsoft::WRL::Make<DODownloadStatusCallback>(progress); + RETURN_IF_NULL_ALLOC(localResult); + + *result = localResult.Detach(); + return S_OK; + } + + // Simply breaks the wait in Wait; the progress object must already be cancelled to force it out. + void Cancel() + { + m_statusCV.notify_all(); + } + + // Returns true on successful completion, false on cancellation, and throws on an error. + bool Wait() + { + std::unique_lock<std::mutex> lock(m_statusMutex); + + // If there is no transfer status update for m_doNoProgressTimeout, we will fail. + auto timeoutTime = std::chrono::steady_clock::now() + Settings::User().Get<Settings::Setting::NetworkDOProgressTimeoutInSeconds>(); + std::optional<UINT64> initialTransferAmount; + bool transferChange = false; + + while (!m_progress.IsCancelled()) + { + if (!transferChange) + { + if (m_statusCV.wait_until(lock, timeoutTime) == std::cv_status::timeout) + { + THROW_HR(DO_E_DOWNLOAD_NO_PROGRESS); + } + } + else + { + m_statusCV.wait(lock); + } + + // Since we just finished a wait, check for cancellation before handling anything else + if (m_progress.IsCancelled()) + { + return false; + } + + AICLI_LOG(Core, Verbose, << "DO State " << m_currentStatus.State << ", " << m_currentStatus.BytesTransferred << " / " << m_currentStatus.BytesTotal << + ", Error 0x" << Logging::SetHRFormat << m_currentStatus.Error << ", extended error 0x" << Logging::SetHRFormat << m_currentStatus.ExtendedError); + + // No matter the state, we are considering any error set to be a failure + if (FAILED(m_currentStatus.Error)) + { + AICLI_LOG(Core, Error, << "DeliveryOptimization error: 0x" << Logging::SetHRFormat << m_currentStatus.Error << + ", extended error: 0x" << Logging::SetHRFormat << m_currentStatus.ExtendedError); + THROW_HR(m_currentStatus.Error); + } + + switch (m_currentStatus.State) + { + // These states are ignored. + case DODownloadState_Created: + case DODownloadState_Paused: + break; + + case DODownloadState_Transferring: + if (m_currentStatus.BytesTransferred || m_currentStatus.BytesTotal) + { + m_progress.OnProgress(m_currentStatus.BytesTransferred, m_currentStatus.BytesTotal, ProgressType::Bytes); + } + + if (!initialTransferAmount) + { + initialTransferAmount = m_currentStatus.BytesTransferred; + } + else if (m_currentStatus.BytesTransferred != initialTransferAmount.value()) + { + transferChange = true; + } + break; + + // These are considered to be 'done' + case DODownloadState_Transferred: + case DODownloadState_Finalized: + if (m_currentStatus.BytesTransferred || m_currentStatus.BytesTotal) + { + m_progress.OnProgress(m_currentStatus.BytesTransferred, m_currentStatus.BytesTotal, ProgressType::Bytes); + } + return true; + + // This is the cancelled state + case DODownloadState_Aborted: + return false; + } + } + + return false; + } + + private: + IProgressCallback& m_progress; + std::mutex m_statusMutex; + std::condition_variable m_statusCV; + DO_DOWNLOAD_STATUS m_currentStatus = {}; + }; + } + + // Debugging tip: + // From an elevated PowerShell, run: + // > Get-DeliveryOptimizationLog | Set-Content doLogs.txt + std::optional<std::vector<BYTE>> DODownload( + const std::string& url, + const std::filesystem::path& dest, + IProgressCallback& progress, + bool computeHash, + std::string_view downloadIdentifier) + { + AICLI_LOG(Core, Info, << "DeliveryOptimization downloading from url: " << url); + + // Remove the target file since DO will not overwrite + std::filesystem::remove(dest); + + DeliveryOptimization::Manager manager; + DeliveryOptimization::Download download = manager.CreateDownload(); + + wil::com_ptr<DeliveryOptimization::DODownloadStatusCallback> callback; + THROW_IF_FAILED(DeliveryOptimization::DODownloadStatusCallback::Create(progress, &callback)); + + download.Uri(url); + download.ContentId(downloadIdentifier); + download.ForegroundPriority(true); + download.LocalPath(dest); + download.CallbackInterface(callback.get()); + + download.Start(); + + auto cancelLifetime = progress.SetCancellationFunction([&download, &callback]() + { + AICLI_LOG(Core, Info, << "Download cancelled."); + download.Cancel(); + callback->Cancel(); + }); + + // Check to handle cancellation between Start and SetCancellationFunction + if (progress.IsCancelled()) + { + AICLI_LOG(Core, Info, << "Download cancelled."); + download.Cancel(); + return {}; + } + + // Wait returns true for success, false for cancellation, and throws on error. + if (callback->Wait()) + { + // Finalize is required to flush the data and change the file name. + download.Finalize(); + AICLI_LOG(Core, Info, << "Download completed."); + + if (computeHash) + { + std::ifstream inStream{ dest, std::ifstream::binary }; + return SHA256::ComputeHash(inStream); + } + } + + return {}; + } +} diff --git a/src/AppInstallerCommonCore/DODownloader.h b/src/AppInstallerCommonCore/DODownloader.h @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <AppInstallerProgress.h> + +#include <optional> +#include <ostream> +#include <string> +#include <vector> + +namespace AppInstaller::Utility +{ + // Downloads a file from the given URL and places it in the given location. + // url: The url to be downloaded from. http->https redirection is allowed. + // dest: The stream to be downloaded to. + // computeHash: Optional. Indicates if SHA256 hash should be calculated when downloading. + std::optional<std::vector<BYTE>> DODownload( + const std::string& url, + const std::filesystem::path& dest, + IProgressCallback& progress, + bool computeHash, + std::string_view downloadIdentifier); +} diff --git a/src/AppInstallerCommonCore/Downloader.cpp b/src/AppInstallerCommonCore/Downloader.cpp @@ -7,20 +7,21 @@ #include "Public/AppInstallerSHA256.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerLogging.h" +#include "Public/winget/UserSettings.h" +#include "DODownloader.h" using namespace AppInstaller::Runtime; +using namespace AppInstaller::Settings; namespace AppInstaller::Utility { - std::optional<std::vector<BYTE>> DownloadToStream( + std::optional<std::vector<BYTE>> WinINetDownloadToStream( const std::string& url, std::ostream& dest, IProgressCallback& progress, bool computeHash) { - THROW_HR_IF(E_INVALIDARG, url.empty()); - - AICLI_LOG(Core, Info, << "Downloading from url: " << url); + AICLI_LOG(Core, Info, << "WinINet downloading from url: " << url); wil::unique_hinternet session(InternetOpenA( "winget-cli", @@ -71,7 +72,6 @@ namespace AppInstaller::Utility // Setup hash engine SHA256 hashEngine; - std::string contentHash; const int bufferSize = 1024 * 1024; // 1MB auto buffer = std::make_unique<BYTE[]>(bufferSize); @@ -128,11 +128,25 @@ namespace AppInstaller::Utility return result; } + std::optional<std::vector<BYTE>> DownloadToStream( + const std::string& url, + std::ostream& dest, + DownloadType, + IProgressCallback& progress, + bool computeHash, + std::string_view) + { + THROW_HR_IF(E_INVALIDARG, url.empty()); + return WinINetDownloadToStream(url, dest, progress, computeHash); + } + std::optional<std::vector<BYTE>> Download( const std::string& url, const std::filesystem::path& dest, + DownloadType type, IProgressCallback& progress, - bool computeHash) + bool computeHash, + std::string_view downloadIdentifier) { THROW_HR_IF(E_INVALIDARG, url.empty()); THROW_HR_IF(E_INVALIDARG, dest.empty()); @@ -141,6 +155,30 @@ namespace AppInstaller::Utility std::filesystem::create_directories(dest.parent_path()); + // Only Installers should be downloaded with DO currently, as: + // - Index :: Constantly changing blob at same location is not what DO is for + // - Manifest :: DO overhead is not needed for small files + // - WinGetUtil :: Intentionally not using DO at this time + if (type == DownloadType::Installer) + { + // Determine whether to try DO first or not, as this is the only choice currently supported. + InstallerDownloader setting = User().Get<Setting::NetworkDownloader>(); + + // Currently, the default remains WinINet until the DO path is proven. + if (setting == InstallerDownloader::Default) + { + setting = InstallerDownloader::WinInet; + } + + if (setting == InstallerDownloader::DeliveryOptimization) + { + return DODownload(url, dest, progress, computeHash, downloadIdentifier); + + // While DO still requires an explicit opt-in, we will let failures through. + // When DO becomes the default, we may choose to catch exceptions and fall back to WinINet below. + } + } + std::ofstream emptyDestFile(dest); emptyDestFile.close(); ApplyMotwIfApplicable(dest, URLZONE_INTERNET); @@ -148,7 +186,7 @@ namespace AppInstaller::Utility // Use std::ofstream::app to append to previous empty file so that it will not // create a new file and clear motw. std::ofstream outfile(dest, std::ofstream::binary | std::ofstream::app); - return DownloadToStream(url, outfile, progress, computeHash); + return WinINetDownloadToStream(url, outfile, progress, computeHash); } using namespace std::string_view_literals; diff --git a/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h b/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h @@ -14,25 +14,41 @@ namespace AppInstaller::Utility { + // The type of data being downloaded; determines what code should + // be used when downloading. + enum class DownloadType + { + Index, + Manifest, + WinGetUtil, + Installer, + }; + // Downloads a file from the given URL and places it in the given location. // url: The url to be downloaded from. http->https redirection is allowed. // dest: The stream to be downloaded to. // computeHash: Optional. Indicates if SHA256 hash should be calculated when downloading. + // downloadIdentifier: Optional. Currently only used by DO to identify the download. std::optional<std::vector<BYTE>> DownloadToStream( const std::string& url, std::ostream& dest, + DownloadType type, IProgressCallback& progress, - bool computeHash = false); + bool computeHash = false, + std::string_view downloadIdentifier = {}); // Downloads a file from the given URL and places it in the given location. // url: The url to be downloaded from. http->https redirection is allowed. // dest: The path to local file to be downloaded to. // computeHash: Optional. Indicates if SHA256 hash should be calculated when downloading. + // downloadIdentifier: Optional. Currently only used by DO to identify the download. std::optional<std::vector<BYTE>> Download( const std::string& url, const std::filesystem::path& dest, + DownloadType type, IProgressCallback& progress, - bool computeHash = false); + bool computeHash = false, + std::string_view downloadIdentifier = {}); // Determines if the given url is a remote location. bool IsUrlRemote(std::string_view url); diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -46,6 +46,14 @@ namespace AppInstaller::Settings Machine, }; + // The download code to use for *installers*. + enum class InstallerDownloader + { + Default, + WinInet, + DeliveryOptimization, + }; + // Enum of settings. // Must start at 0 to enable direct access to variant in UserSettings. // Max must be last and unused. @@ -68,6 +76,8 @@ namespace AppInstaller::Settings EFRestSource, InstallScopePreference, InstallScopeRequirement, + NetworkDownloader, + NetworkDOProgressTimeoutInSeconds, Max }; @@ -114,6 +124,8 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::EFRestSource, bool, bool, false, ".experimentalFeatures.restSource"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallScopePreference, std::string, ScopePreference, ScopePreference::User, ".installBehavior.preferences.scope"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallScopeRequirement, std::string, ScopePreference, ScopePreference::None, ".installBehavior.requirements.scope"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::NetworkDownloader, std::string, InstallerDownloader, InstallerDownloader::Default, ".network.downloader"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::NetworkDOProgressTimeoutInSeconds, uint32_t, std::chrono::seconds, 20s, ".network.doProgressTimeoutInSeconds"sv); // Used to deduce the SettingVariant type; making a variant that includes std::monostate and all SettingMapping types. template <size_t... I> diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp @@ -227,6 +227,33 @@ namespace AppInstaller::Settings { return SettingMapping<Setting::InstallScopePreference>::Validate(value); } + + WINGET_VALIDATE_SIGNATURE(NetworkDownloader) + { + static constexpr std::string_view s_downloader_default = "default"; + static constexpr std::string_view s_downloader_wininet = "wininet"; + static constexpr std::string_view s_downloader_do = "do"; + + if (Utility::CaseInsensitiveEquals(value, s_downloader_default)) + { + return InstallerDownloader::Default; + } + else if (Utility::CaseInsensitiveEquals(value, s_downloader_wininet)) + { + return InstallerDownloader::WinInet; + } + else if (Utility::CaseInsensitiveEquals(value, s_downloader_do)) + { + return InstallerDownloader::DeliveryOptimization; + } + + return {}; + } + + WINGET_VALIDATE_SIGNATURE(NetworkDOProgressTimeoutInSeconds) + { + return std::chrono::seconds(value); + } } #ifndef AICLI_DISABLE_TEST_HOOKS diff --git a/src/AppInstallerCommonCore/external/README.md b/src/AppInstallerCommonCore/external/README.md @@ -0,0 +1,3 @@ +This is a temporary location to store headers from external sources: +1. do.h + - This header for the DeliveryOptimization COM API is not included in the Windows SDK currently, but should be in the near future. While we wait for this fix, we will use this file.+ \ No newline at end of file diff --git a/src/AppInstallerCommonCore/external/do.h b/src/AppInstallerCommonCore/external/do.h @@ -0,0 +1,566 @@ + + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 8.01.0626 */ +/* @@MIDL_FILE_HEADING( ) */ + + + +/* verify that the <rpcndr.h> version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 500 +#endif + +/* verify that the <rpcsal.h> version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCSAL_H_VERSION__ +#define __REQUIRED_RPCSAL_H_VERSION__ 100 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of <rpcndr.h> +#endif /* __RPCNDR_H_VERSION__ */ + +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __do_h__ +#define __do_h__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#ifndef DECLSPEC_XFGVIRT +#if _CONTROL_FLOW_GUARD_XFG +#define DECLSPEC_XFGVIRT(base, func) __declspec(xfg_virtual(base, func)) +#else +#define DECLSPEC_XFGVIRT(base, func) +#endif +#endif + +/* Forward Declarations */ + +#ifndef __IDODownload_FWD_DEFINED__ +#define __IDODownload_FWD_DEFINED__ +typedef interface IDODownload IDODownload; + +#endif /* __IDODownload_FWD_DEFINED__ */ + + +#ifndef __IDODownloadStatusCallback_FWD_DEFINED__ +#define __IDODownloadStatusCallback_FWD_DEFINED__ +typedef interface IDODownloadStatusCallback IDODownloadStatusCallback; + +#endif /* __IDODownloadStatusCallback_FWD_DEFINED__ */ + + +#ifndef __IDOManager_FWD_DEFINED__ +#define __IDOManager_FWD_DEFINED__ +typedef interface IDOManager IDOManager; + +#endif /* __IDOManager_FWD_DEFINED__ */ + + +#ifndef __DeliveryOptimization_FWD_DEFINED__ +#define __DeliveryOptimization_FWD_DEFINED__ + +#ifdef __cplusplus +typedef class DeliveryOptimization DeliveryOptimization; +#else +typedef struct DeliveryOptimization DeliveryOptimization; +#endif /* __cplusplus */ + +#endif /* __DeliveryOptimization_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "oaidl.h" + +#ifdef __cplusplus +extern "C"{ +#endif + + +/* interface __MIDL_itf_do_0000_0000 */ +/* [local] */ + +typedef struct _DO_DOWNLOAD_RANGE + { + UINT64 Offset; + UINT64 Length; + } DO_DOWNLOAD_RANGE; + +typedef struct _DO_DOWNLOAD_RANGES_INFO + { + UINT RangeCount; + /* [size_is] */ DO_DOWNLOAD_RANGE Ranges[ 1 ]; + } DO_DOWNLOAD_RANGES_INFO; + +typedef +enum _DODownloadState + { + DODownloadState_Created = 0, + DODownloadState_Transferring = ( DODownloadState_Created + 1 ) , + DODownloadState_Transferred = ( DODownloadState_Transferring + 1 ) , + DODownloadState_Finalized = ( DODownloadState_Transferred + 1 ) , + DODownloadState_Aborted = ( DODownloadState_Finalized + 1 ) , + DODownloadState_Paused = ( DODownloadState_Aborted + 1 ) + } DODownloadState; + +typedef struct _DO_DOWNLOAD_STATUS + { + UINT64 BytesTotal; + UINT64 BytesTransferred; + DODownloadState State; + HRESULT Error; + HRESULT ExtendedError; + } DO_DOWNLOAD_STATUS; + +typedef +enum _DODownloadCostPolicy + { + DODownloadCostPolicy_Always = 0, + DODownloadCostPolicy_Unrestricted = ( DODownloadCostPolicy_Always + 1 ) , + DODownloadCostPolicy_Standard = ( DODownloadCostPolicy_Unrestricted + 1 ) , + DODownloadCostPolicy_NoRoaming = ( DODownloadCostPolicy_Standard + 1 ) , + DODownloadCostPolicy_NoSurcharge = ( DODownloadCostPolicy_NoRoaming + 1 ) , + DODownloadCostPolicy_NoCellular = ( DODownloadCostPolicy_NoSurcharge + 1 ) + } DODownloadCostPolicy; + +typedef +enum _DODownloadProperty + { + DODownloadProperty_Id = 0, + DODownloadProperty_Uri = ( DODownloadProperty_Id + 1 ) , + DODownloadProperty_ContentId = ( DODownloadProperty_Uri + 1 ) , + DODownloadProperty_DisplayName = ( DODownloadProperty_ContentId + 1 ) , + DODownloadProperty_LocalPath = ( DODownloadProperty_DisplayName + 1 ) , + DODownloadProperty_HttpCustomHeaders = ( DODownloadProperty_LocalPath + 1 ) , + DODownloadProperty_CostPolicy = ( DODownloadProperty_HttpCustomHeaders + 1 ) , + DODownloadProperty_SecurityFlags = ( DODownloadProperty_CostPolicy + 1 ) , + DODownloadProperty_CallbackFreqPercent = ( DODownloadProperty_SecurityFlags + 1 ) , + DODownloadProperty_CallbackFreqSeconds = ( DODownloadProperty_CallbackFreqPercent + 1 ) , + DODownloadProperty_NoProgressTimeoutSeconds = ( DODownloadProperty_CallbackFreqSeconds + 1 ) , + DODownloadProperty_ForegroundPriority = ( DODownloadProperty_NoProgressTimeoutSeconds + 1 ) , + DODownloadProperty_BlockingMode = ( DODownloadProperty_ForegroundPriority + 1 ) , + DODownloadProperty_CallbackInterface = ( DODownloadProperty_BlockingMode + 1 ) , + DODownloadProperty_StreamInterface = ( DODownloadProperty_CallbackInterface + 1 ) , + DODownloadProperty_SecurityContext = ( DODownloadProperty_StreamInterface + 1 ) , + DODownloadProperty_NetworkToken = ( DODownloadProperty_SecurityContext + 1 ) , + DODownloadProperty_CorrelationVector = ( DODownloadProperty_NetworkToken + 1 ) , + DODownloadProperty_DecryptionInfo = ( DODownloadProperty_CorrelationVector + 1 ) , + DODownloadProperty_IntegrityCheckInfo = ( DODownloadProperty_DecryptionInfo + 1 ) , + DODownloadProperty_IntegrityCheckMandatory = ( DODownloadProperty_IntegrityCheckInfo + 1 ) , + DODownloadProperty_TotalSizeBytes = ( DODownloadProperty_IntegrityCheckMandatory + 1 ) , + DODownloadProperty_DisallowOnCellular = ( DODownloadProperty_TotalSizeBytes + 1 ) , + DODownloadProperty_HttpCustomAuthHeaders = ( DODownloadProperty_DisallowOnCellular + 1 ) + } DODownloadProperty; + +typedef struct _DO_DOWNLOAD_ENUM_CATEGORY + { + DODownloadProperty Property; + LPCWSTR Value; + } DO_DOWNLOAD_ENUM_CATEGORY; + + + +extern RPC_IF_HANDLE __MIDL_itf_do_0000_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_do_0000_0000_v0_0_s_ifspec; + +#ifndef __IDODownload_INTERFACE_DEFINED__ +#define __IDODownload_INTERFACE_DEFINED__ + +/* interface IDODownload */ +/* [uuid][object] */ + + +EXTERN_C const IID IID_IDODownload; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("FBBD7FC0-C147-4727-A38D-827EF071EE77") + IDODownload : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Start( + /* [unique][in] */ __RPC__in_opt DO_DOWNLOAD_RANGES_INFO *ranges) = 0; + + virtual HRESULT STDMETHODCALLTYPE Pause( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Abort( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Finalize( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStatus( + /* [out] */ __RPC__out DO_DOWNLOAD_STATUS *status) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetProperty( + /* [in] */ DODownloadProperty propId, + /* [out] */ __RPC__out VARIANT *propVal) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProperty( + /* [in] */ DODownloadProperty propId, + /* [in] */ __RPC__in VARIANT *propVal) = 0; + + }; + + +#else /* C style interface */ + + typedef struct IDODownloadVtbl + { + BEGIN_INTERFACE + + DECLSPEC_XFGVIRT(IUnknown, QueryInterface) + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + __RPC__in IDODownload * This, + /* [in] */ __RPC__in REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + DECLSPEC_XFGVIRT(IUnknown, AddRef) + ULONG ( STDMETHODCALLTYPE *AddRef )( + __RPC__in IDODownload * This); + + DECLSPEC_XFGVIRT(IUnknown, Release) + ULONG ( STDMETHODCALLTYPE *Release )( + __RPC__in IDODownload * This); + + DECLSPEC_XFGVIRT(IDODownload, Start) + HRESULT ( STDMETHODCALLTYPE *Start )( + __RPC__in IDODownload * This, + /* [unique][in] */ __RPC__in_opt DO_DOWNLOAD_RANGES_INFO *ranges); + + DECLSPEC_XFGVIRT(IDODownload, Pause) + HRESULT ( STDMETHODCALLTYPE *Pause )( + __RPC__in IDODownload * This); + + DECLSPEC_XFGVIRT(IDODownload, Abort) + HRESULT ( STDMETHODCALLTYPE *Abort )( + __RPC__in IDODownload * This); + + DECLSPEC_XFGVIRT(IDODownload, Finalize) + HRESULT ( STDMETHODCALLTYPE *Finalize )( + __RPC__in IDODownload * This); + + DECLSPEC_XFGVIRT(IDODownload, GetStatus) + HRESULT ( STDMETHODCALLTYPE *GetStatus )( + __RPC__in IDODownload * This, + /* [out] */ __RPC__out DO_DOWNLOAD_STATUS *status); + + DECLSPEC_XFGVIRT(IDODownload, GetProperty) + HRESULT ( STDMETHODCALLTYPE *GetProperty )( + __RPC__in IDODownload * This, + /* [in] */ DODownloadProperty propId, + /* [out] */ __RPC__out VARIANT *propVal); + + DECLSPEC_XFGVIRT(IDODownload, SetProperty) + HRESULT ( STDMETHODCALLTYPE *SetProperty )( + __RPC__in IDODownload * This, + /* [in] */ DODownloadProperty propId, + /* [in] */ __RPC__in VARIANT *propVal); + + END_INTERFACE + } IDODownloadVtbl; + + interface IDODownload + { + CONST_VTBL struct IDODownloadVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IDODownload_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define IDODownload_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define IDODownload_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define IDODownload_Start(This,ranges) \ + ( (This)->lpVtbl -> Start(This,ranges) ) + +#define IDODownload_Pause(This) \ + ( (This)->lpVtbl -> Pause(This) ) + +#define IDODownload_Abort(This) \ + ( (This)->lpVtbl -> Abort(This) ) + +#define IDODownload_Finalize(This) \ + ( (This)->lpVtbl -> Finalize(This) ) + +#define IDODownload_GetStatus(This,status) \ + ( (This)->lpVtbl -> GetStatus(This,status) ) + +#define IDODownload_GetProperty(This,propId,propVal) \ + ( (This)->lpVtbl -> GetProperty(This,propId,propVal) ) + +#define IDODownload_SetProperty(This,propId,propVal) \ + ( (This)->lpVtbl -> SetProperty(This,propId,propVal) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __IDODownload_INTERFACE_DEFINED__ */ + + +#ifndef __IDODownloadStatusCallback_INTERFACE_DEFINED__ +#define __IDODownloadStatusCallback_INTERFACE_DEFINED__ + +/* interface IDODownloadStatusCallback */ +/* [uuid][object] */ + + +EXTERN_C const IID IID_IDODownloadStatusCallback; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("D166E8E3-A90E-4392-8E87-05E996D3747D") + IDODownloadStatusCallback : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnStatusChange( + /* [in] */ __RPC__in_opt IDODownload *download, + /* [in] */ __RPC__in DO_DOWNLOAD_STATUS *status) = 0; + + }; + + +#else /* C style interface */ + + typedef struct IDODownloadStatusCallbackVtbl + { + BEGIN_INTERFACE + + DECLSPEC_XFGVIRT(IUnknown, QueryInterface) + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + __RPC__in IDODownloadStatusCallback * This, + /* [in] */ __RPC__in REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + DECLSPEC_XFGVIRT(IUnknown, AddRef) + ULONG ( STDMETHODCALLTYPE *AddRef )( + __RPC__in IDODownloadStatusCallback * This); + + DECLSPEC_XFGVIRT(IUnknown, Release) + ULONG ( STDMETHODCALLTYPE *Release )( + __RPC__in IDODownloadStatusCallback * This); + + DECLSPEC_XFGVIRT(IDODownloadStatusCallback, OnStatusChange) + HRESULT ( STDMETHODCALLTYPE *OnStatusChange )( + __RPC__in IDODownloadStatusCallback * This, + /* [in] */ __RPC__in_opt IDODownload *download, + /* [in] */ __RPC__in DO_DOWNLOAD_STATUS *status); + + END_INTERFACE + } IDODownloadStatusCallbackVtbl; + + interface IDODownloadStatusCallback + { + CONST_VTBL struct IDODownloadStatusCallbackVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IDODownloadStatusCallback_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define IDODownloadStatusCallback_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define IDODownloadStatusCallback_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define IDODownloadStatusCallback_OnStatusChange(This,download,status) \ + ( (This)->lpVtbl -> OnStatusChange(This,download,status) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __IDODownloadStatusCallback_INTERFACE_DEFINED__ */ + + +#ifndef __IDOManager_INTERFACE_DEFINED__ +#define __IDOManager_INTERFACE_DEFINED__ + +/* interface IDOManager */ +/* [uuid][object] */ + + +EXTERN_C const IID IID_IDOManager; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("400E2D4A-1431-4C1A-A748-39CA472CFDB1") + IDOManager : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE CreateDownload( + /* [out] */ __RPC__deref_out_opt IDODownload **download) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumDownloads( + /* [unique][in] */ __RPC__in_opt DO_DOWNLOAD_ENUM_CATEGORY *category, + /* [out] */ __RPC__deref_out_opt IEnumUnknown **ppEnum) = 0; + + }; + + +#else /* C style interface */ + + typedef struct IDOManagerVtbl + { + BEGIN_INTERFACE + + DECLSPEC_XFGVIRT(IUnknown, QueryInterface) + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + __RPC__in IDOManager * This, + /* [in] */ __RPC__in REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + DECLSPEC_XFGVIRT(IUnknown, AddRef) + ULONG ( STDMETHODCALLTYPE *AddRef )( + __RPC__in IDOManager * This); + + DECLSPEC_XFGVIRT(IUnknown, Release) + ULONG ( STDMETHODCALLTYPE *Release )( + __RPC__in IDOManager * This); + + DECLSPEC_XFGVIRT(IDOManager, CreateDownload) + HRESULT ( STDMETHODCALLTYPE *CreateDownload )( + __RPC__in IDOManager * This, + /* [out] */ __RPC__deref_out_opt IDODownload **download); + + DECLSPEC_XFGVIRT(IDOManager, EnumDownloads) + HRESULT ( STDMETHODCALLTYPE *EnumDownloads )( + __RPC__in IDOManager * This, + /* [unique][in] */ __RPC__in_opt DO_DOWNLOAD_ENUM_CATEGORY *category, + /* [out] */ __RPC__deref_out_opt IEnumUnknown **ppEnum); + + END_INTERFACE + } IDOManagerVtbl; + + interface IDOManager + { + CONST_VTBL struct IDOManagerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IDOManager_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define IDOManager_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define IDOManager_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define IDOManager_CreateDownload(This,download) \ + ( (This)->lpVtbl -> CreateDownload(This,download) ) + +#define IDOManager_EnumDownloads(This,category,ppEnum) \ + ( (This)->lpVtbl -> EnumDownloads(This,category,ppEnum) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __IDOManager_INTERFACE_DEFINED__ */ + + + +#ifndef __DeliveryOptimization_LIBRARY_DEFINED__ +#define __DeliveryOptimization_LIBRARY_DEFINED__ + +/* library DeliveryOptimization */ +/* [uuid] */ + + +EXTERN_C const IID LIBID_DeliveryOptimization; + +EXTERN_C const CLSID CLSID_DeliveryOptimization; + +#ifdef __cplusplus + +class DECLSPEC_UUID("5b99fa76-721c-423c-adac-56d03c8a8007") +DeliveryOptimization; +#endif +#endif /* __DeliveryOptimization_LIBRARY_DEFINED__ */ + +/* interface __MIDL_itf_do_0000_0004 */ +/* [local] */ + +#define DO_LENGTH_TO_EOF (UINT64)(-1) + +#define DecryptionInfo_KeyData L"KeyData" +#define DecryptionInfo_EncryptionBufferSize L"EncryptionBufferSize" +#define DecryptionInfo_AlgorithmName L"AlgorithmName" +#define DecryptionInfo_ChainingMode L"ChainingMode" + +#define IntegrityCheckInfo_PiecesHashFileUrl L"PiecesHashFileUrl" +#define IntegrityCheckInfo_PiecesHashFileDigest L"PiecesHashFileDigest" +#define IntegrityCheckInfo_PiecesHashFileDigestAlgorithm L"PiecesHashFileDigestAlgorithm" +#define IntegrityCheckInfo_HashOfHashes L"HashOfHashes" + + +extern RPC_IF_HANDLE __MIDL_itf_do_0000_0004_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_do_0000_0004_v0_0_s_ifspec; + +/* Additional Prototypes for ALL interfaces */ + +unsigned long __RPC_USER VARIANT_UserSize( __RPC__in unsigned long *, unsigned long , __RPC__in VARIANT * ); +unsigned char * __RPC_USER VARIANT_UserMarshal( __RPC__in unsigned long *, __RPC__inout_xcount(0) unsigned char *, __RPC__in VARIANT * ); +unsigned char * __RPC_USER VARIANT_UserUnmarshal(__RPC__in unsigned long *, __RPC__in_xcount(0) unsigned char *, __RPC__out VARIANT * ); +void __RPC_USER VARIANT_UserFree( __RPC__in unsigned long *, __RPC__in VARIANT * ); + +unsigned long __RPC_USER VARIANT_UserSize64( __RPC__in unsigned long *, unsigned long , __RPC__in VARIANT * ); +unsigned char * __RPC_USER VARIANT_UserMarshal64( __RPC__in unsigned long *, __RPC__inout_xcount(0) unsigned char *, __RPC__in VARIANT * ); +unsigned char * __RPC_USER VARIANT_UserUnmarshal64(__RPC__in unsigned long *, __RPC__in_xcount(0) unsigned char *, __RPC__out VARIANT * ); +void __RPC_USER VARIANT_UserFree64( __RPC__in unsigned long *, __RPC__in VARIANT * ); + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/src/AppInstallerCommonCore/pch.h b/src/AppInstallerCommonCore/pch.h @@ -29,6 +29,7 @@ #include <algorithm> #include <chrono> +#include <condition_variable> #include <cwctype> #include <filesystem> #include <fstream> @@ -38,6 +39,7 @@ #include <iterator> #include <limits> #include <memory> +#include <mutex> #include <ostream> #include <regex> #include <set> @@ -55,6 +57,7 @@ #include <wil/result_macros.h> #include <wil/safecast.h> #include <wil/token_helpers.h> +#include <wil/com.h> #pragma warning( pop ) #ifndef WINGET_DISABLE_FOR_FUZZING @@ -75,6 +78,7 @@ #endif #include <wrl/client.h> +#include <wrl/implements.h> // Stream/buffer helper APIs #include <robuffer.h> diff --git a/src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp b/src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp @@ -192,7 +192,7 @@ namespace AppInstaller::Repository::Microsoft tempFile = Runtime::GetPathTo(Runtime::PathName::Temp); tempFile /= GetPackageFamilyNameFromDetails(details) + ".msix"; - Utility::Download(packageLocation, tempFile, progress); + Utility::Download(packageLocation, tempFile, Utility::DownloadType::Index, progress); uri = winrt::Windows::Foundation::Uri(tempFile.c_str()); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -113,7 +113,7 @@ namespace AppInstaller::Repository::Microsoft bool success = false; try { - (void)Utility::DownloadToStream(fullPath, manifestStream, emptyCallback); + (void)Utility::DownloadToStream(fullPath, manifestStream, Utility::DownloadType::Manifest, emptyCallback); success = true; } catch (...) diff --git a/src/WinGetUtil/Exports.cpp b/src/WinGetUtil/Exports.cpp @@ -242,7 +242,7 @@ extern "C" THROW_HR_IF(E_INVALIDARG, computeHash && sha256HashLength != 32); AppInstaller::ProgressCallback callback; - auto hashValue = Download(ConvertToUTF8(url), filePath, callback, computeHash); + auto hashValue = Download(ConvertToUTF8(url), filePath, DownloadType::WinGetUtil, callback, computeHash); // At this point, if computeHash is set we have verified that the buffer is valid and 32 bytes. if (computeHash)