commit 5ab4b1e414d342e5da4a1cc432bfcc11fb8c5050 parent 02e14be0a04d618c1c769d166dae185d8b9b58a1 Author: yao-msft <50888816+yao-msft@users.noreply.github.com> Date: Mon, 26 Oct 2020 16:55:52 -0700 Improve Motw related experience (#625) Diffstat:
19 files changed, 288 insertions(+), 52 deletions(-)
diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h @@ -47,6 +47,7 @@ namespace AppInstaller::CLI::Execution SearchResult, SourceList, Manifest, + PackageVersion, Installer, HashPair, InstallerPath, @@ -100,6 +101,12 @@ namespace AppInstaller::CLI::Execution }; template <> + struct DataMapping<Data::PackageVersion> + { + using value_t = std::shared_ptr<Repository::IPackageVersion>; + }; + + template <> struct DataMapping<Data::Installer> { using value_t = std::optional<Manifest::ManifestInstaller>; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -15,6 +15,7 @@ namespace AppInstaller::CLI::Workflow using namespace winrt::Windows::Management::Deployment; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; + using namespace AppInstaller::Repository; void EnsureMinOSVersion(Execution::Context& context) { @@ -86,6 +87,8 @@ namespace AppInstaller::CLI::Workflow default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } + + context << UpdateInstallerFileMotwIfApplicable; } void DownloadInstallerFile(Execution::Context& context) @@ -189,6 +192,29 @@ namespace AppInstaller::CLI::Workflow } } + void UpdateInstallerFileMotwIfApplicable(Execution::Context& context) + { + if (context.Contains(Execution::Data::InstallerPath)) + { + // Only update Motw if installer hash matches + const auto& hashPair = context.Get<Execution::Data::HashPair>(); + if (std::equal(hashPair.first.begin(), hashPair.first.end(), hashPair.second.begin())) + { + if (context.Contains(Execution::Data::PackageVersion) && + context.Get<Execution::Data::PackageVersion>()->GetSource() != nullptr && + SourceTrustLevel::Trusted == context.Get<Execution::Data::PackageVersion>()->GetSource()->GetDetails().TrustLevel) + { + Utility::ApplyMotwIfApplicable(context.Get<Execution::Data::InstallerPath>(), URLZONE_TRUSTED); + } + else + { + const auto& installer = context.Get<Execution::Data::Installer>(); + Utility::ApplyMotwUsingIAttachmentExecuteIfApplicable(context.Get<Execution::Data::InstallerPath>(), installer.value().Url); + } + } + } + } + void ExecuteInstaller(Execution::Context& context) { const auto& installer = context.Get<Execution::Data::Installer>().value(); @@ -273,7 +299,20 @@ namespace AppInstaller::CLI::Workflow { const auto& path = context.Get<Execution::Data::InstallerPath>(); AICLI_LOG(CLI, Info, << "Removing installer: " << path); - std::filesystem::remove(path); + + try + { + // best effort + std::filesystem::remove(path); + } + catch (const std::exception& e) + { + AICLI_LOG(CLI, Warning, << "Failed to remove installer file after execution. Reason: " << e.what()); + } + catch (...) + { + AICLI_LOG(CLI, Warning, << "Failed to remove installer file after execution. Reason unknown."); + } } } } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -53,6 +53,12 @@ namespace AppInstaller::CLI::Workflow // Outputs: SourceList void VerifyInstallerHash(Execution::Context& context); + // Update Motw of the downloaded installer if applicable + // Required Args: None + // Inputs: HashPair, InstallerPath?, SourceId? + // Outputs: None + void UpdateInstallerFileMotwIfApplicable(Execution::Context& context); + // Composite flow that chooses what to do based on the installer type. // Required Args: None // Inputs: Installer, InstallerPath diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -33,7 +33,8 @@ namespace AppInstaller::CLI::Workflow // Check Update Version if (IsUpdateVersionApplicable(installedVersion, Utility::Version(key.Version))) { - auto manifest = m_package.GetAvailableVersion(key)->GetManifest(); + auto packageVersion = m_package.GetAvailableVersion(key); + auto manifest = packageVersion->GetManifest(); // Check MinOSVersion if (!manifest.MinOSVersion.empty() && @@ -51,6 +52,7 @@ namespace AppInstaller::CLI::Workflow // Since we already did installer selection, just populate the context Data context.Add<Execution::Data::Manifest>(std::move(manifest)); + context.Add<Execution::Data::PackageVersion>(std::move(packageVersion)); context.Add<Execution::Data::Installer>(std::move(installer)); updateFound = true; diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -460,6 +460,7 @@ namespace AppInstaller::CLI::Workflow Logging::Telemetry().LogManifestFields(manifest->Id, manifest->Name, manifest->Version); context.Add<Execution::Data::Manifest>(std::move(manifest.value())); + context.Add<Execution::Data::PackageVersion>(std::move(requestedVersion)); } void VerifyFile::operator()(Execution::Context& context) const diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp @@ -57,6 +57,11 @@ namespace TestCommon return VersionManifest; } + std::shared_ptr<const ISource> TestPackageVersion::GetSource() const + { + return Source; + } + TestPackageVersion::MetadataMap TestPackageVersion::GetMetadata() const { return Metadata; diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h @@ -13,6 +13,7 @@ namespace TestCommon struct TestPackageVersion : public AppInstaller::Repository::IPackageVersion { using Manifest = AppInstaller::Manifest::Manifest; + using ISource = AppInstaller::Repository::ISource; using LocIndString = AppInstaller::Utility::LocIndString; using MetadataMap = AppInstaller::Repository::IPackageVersion::Metadata; @@ -27,10 +28,12 @@ namespace TestCommon LocIndString GetProperty(AppInstaller::Repository::PackageVersionProperty property) const override; std::vector<LocIndString> GetMultiProperty(AppInstaller::Repository::PackageVersionMultiProperty property) const override; Manifest GetManifest() const override; + std::shared_ptr<const ISource> GetSource() const override; MetadataMap GetMetadata() const override; Manifest VersionManifest; MetadataMap Metadata; + std::shared_ptr<const ISource> Source; protected: static void AddFoldedIfHasValueAndNotPresent(const AppInstaller::Utility::NormalizedString& value, std::vector<LocIndString>& target); diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp @@ -287,6 +287,13 @@ void OverrideForCompositeInstalledSource(TestContext& context) } }); } +void OverrideForUpdateInstallerMotw(TestContext& context) +{ + context.Override({ UpdateInstallerFileMotwIfApplicable, [](TestContext&) + { + } }); +} + void OverrideForShellExecute(TestContext& context) { context.Override({ DownloadInstallerFile, [](TestContext& context) @@ -298,6 +305,8 @@ void OverrideForShellExecute(TestContext& context) context.Override({ RenameDownloadedInstaller, [](TestContext&) { } }); + + OverrideForUpdateInstallerMotw(context); } void OverrideForMSIX(TestContext& context) @@ -425,6 +434,7 @@ TEST_CASE("MsixInstallFlow_DownloadFlow", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideForMSIX(context); + OverrideForUpdateInstallerMotw(context); // Todo: point to files from our repo when the repo goes public context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Msix_DownloadFlow.yaml").GetPath().u8string()); @@ -928,4 +938,50 @@ TEST_CASE("UpdateFlow_UpdateAllApplicable", "[UpdateFlow][workflow]") REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath())); REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath())); REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath())); +} + +void VerifyMotw(const std::filesystem::path& testFile, DWORD zone) +{ + std::filesystem::path motwFile(testFile); + motwFile += ":Zone.Identifier:$data"; + std::ifstream motwStream(motwFile); + std::stringstream motwContent; + motwContent << motwStream.rdbuf(); + std::string motwContentStr = motwContent.str(); + motwStream.close(); + REQUIRE(motwContentStr.find("ZoneId=" + std::to_string(zone)) != std::string::npos); +} + +TEST_CASE("UpdateInstallerFileMotw", "[DownloadInstaller][workflow]") +{ + TestCommon::TempFile testInstallerPath("TestInstaller.txt"); + + std::ofstream ofile(testInstallerPath, std::ofstream::out); + ofile << "test"; + ofile.close(); + + ApplyMotwIfApplicable(testInstallerPath, URLZONE_INTERNET); + VerifyMotw(testInstallerPath, 3); + + std::ostringstream updateMotwOutput; + TestContext context{ updateMotwOutput, std::cin }; + context.Add<Data::HashPair>({ {}, {} }); + context.Add<Data::InstallerPath>(testInstallerPath); + auto packageVersion = std::make_shared<TestPackageVersion>(Manifest{}); + auto testSource = std::make_shared<TestSource>(); + testSource->Details.TrustLevel = SourceTrustLevel::Trusted; + packageVersion->Source = testSource; + context.Add<Data::PackageVersion>(packageVersion); + ManifestInstaller installer; + installer.Url = "http://NotTrusted.com"; + context.Add<Data::Installer>(std::move(installer)); + + UpdateInstallerFileMotwIfApplicable(context); + VerifyMotw(testInstallerPath, 2); + + testSource->Details.TrustLevel = SourceTrustLevel::None; + UpdateInstallerFileMotwIfApplicable(context); + VerifyMotw(testInstallerPath, 3); + + INFO(updateMotwOutput.str()); } \ No newline at end of file diff --git a/src/AppInstallerCLITests/pch.h b/src/AppInstallerCLITests/pch.h @@ -5,6 +5,7 @@ #include <Windows.h> #include <WinInet.h> #include <shellapi.h> +#include <urlmon.h> #include <catch.hpp> diff --git a/src/AppInstallerCommonCore/Downloader.cpp b/src/AppInstallerCommonCore/Downloader.cpp @@ -136,7 +136,7 @@ namespace AppInstaller::Utility std::ofstream emptyDestFile(dest); emptyDestFile.close(); - ApplyMotwIfApplicable(dest); + ApplyMotwIfApplicable(dest, URLZONE_INTERNET); // Use std::ofstream::app to append to previous empty file so that it will not // create a new file and clear motw. @@ -171,51 +171,64 @@ namespace AppInstaller::Utility return false; } - void ApplyMotwIfApplicable(const std::filesystem::path& filePath) + void ApplyMotwIfApplicable(const std::filesystem::path& filePath, URLZONE zone) { - AICLI_LOG(Core, Info, << "Started applying motw to " << filePath); + AICLI_LOG(Core, Info, << "Started applying motw to " << filePath << " with zone: " << zone); + if (!IsNTFS(filePath)) { - // Check the file system the input file is on. - wil::unique_hfile fileHandle{ CreateFileW( - filePath.c_str(), /*lpFileName*/ - GENERIC_READ, /*dwDesiredAccess*/ - 0, /*dwShareMode*/ - NULL, /*lpSecurityAttributes*/ - OPEN_EXISTING, /*dwCreationDisposition*/ - FILE_ATTRIBUTE_NORMAL, /*dwFlagsAndAttributes*/ - NULL /*hTemplateFile*/) }; - - THROW_LAST_ERROR_IF(fileHandle.get() == INVALID_HANDLE_VALUE); - - wchar_t fileSystemName[MAX_PATH]; - THROW_LAST_ERROR_IF(!GetVolumeInformationByHandleW( - fileHandle.get(), /*hFile*/ - NULL, /*lpVolumeNameBuffer*/ - 0, /*nVolumeNameSize*/ - NULL, /*lpVolumeSerialNumber*/ - NULL, /*lpMaximumComponentLength*/ - NULL, /*lpFileSystemFlags*/ - fileSystemName, /*lpFileSystemNameBuffer*/ - MAX_PATH /*nFileSystemNameSize*/)); - - if (_wcsicmp(fileSystemName, L"NTFS") != 0) - { - AICLI_LOG(Core, Info, << "File system is not NTFS. Skipped applying motw"); - return; - } + AICLI_LOG(Core, Info, << "File system is not NTFS. Skipped applying motw"); + return; } - // Zone Identifier stream name - // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8 - std::filesystem::path motwPath(filePath); - motwPath += L":Zone.Identifier:$DATA"; + Microsoft::WRL::ComPtr<IZoneIdentifier> zoneIdentifier; + THROW_IF_FAILED(CoCreateInstance(CLSID_PersistentZoneIdentifier, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&zoneIdentifier))); + THROW_IF_FAILED(zoneIdentifier->SetId(zone)); - // Apply mark of the web. ZoneId 3 means downloaded from internet. - std::ofstream motwStream(motwPath); - motwStream << "[ZoneTransfer]" << std::endl; - motwStream << "ZoneId=3" << std::endl; + Microsoft::WRL::ComPtr<IPersistFile> persistFile; + THROW_IF_FAILED(zoneIdentifier.As(&persistFile)); + THROW_IF_FAILED(persistFile->Save(filePath.c_str(), TRUE)); AICLI_LOG(Core, Info, << "Finished applying motw"); } + + void ApplyMotwUsingIAttachmentExecuteIfApplicable(const std::filesystem::path& filePath, const std::string& source) + { + AICLI_LOG(Core, Info, << "Started applying motw using IAttachmentExecute to " << filePath); + + if (!IsNTFS(filePath)) + { + AICLI_LOG(Core, Info, << "File system is not NTFS. Skipped applying motw"); + return; + } + + // Attachment execution service needs STA to succeed, so we'll create a new thread and CoInitialize with STA. + auto updateMotw = [&]() -> HRESULT + { + Microsoft::WRL::ComPtr<IAttachmentExecute> attachmentExecute; + RETURN_IF_FAILED(CoCreateInstance(CLSID_AttachmentServices, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&attachmentExecute))); + RETURN_IF_FAILED(attachmentExecute->SetLocalPath(filePath.c_str())); + RETURN_IF_FAILED(attachmentExecute->SetSource(Utility::ConvertToUTF16(source).c_str())); + RETURN_IF_FAILED(attachmentExecute->Save()); + return S_OK; + }; + + HRESULT hr = S_OK; + + std::thread aesThread([&]() + { + hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + if (FAILED(hr)) + { + return; + } + + hr = updateMotw(); + CoUninitialize(); + }); + + aesThread.join(); + + AICLI_LOG(Core, Info, << "Finished applying motw using IAttachmentExecute. Result: " << hr); + } } diff --git a/src/AppInstallerCommonCore/Errors.cpp b/src/AppInstallerCommonCore/Errors.cpp @@ -22,6 +22,8 @@ namespace AppInstaller return "Executing command failed"; case APPINSTALLER_CLI_ERROR_MANIFEST_FAILED: return "Opening manifest failed"; + case APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED: + return "Cancellation signal received"; case APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED: return "Running ShellExecute failed"; case APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION: @@ -58,10 +60,48 @@ namespace AppInstaller return "Multiple packages found matching the criteria"; case APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND: return "No manifest found matching the criteria"; + case APPINSTALLER_CLI_ERROR_EXTENSION_PUBLIC_FAILED: + return "Failed to get Public folder from source package"; case APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN: return "Command requires administrator privileges to run"; case APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE: return "The source location is not secure"; + case APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY: + return "The Microsoft Store client is blocked by policy"; + case APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY: + return "The Microsoft Store app is blocked by policy"; + case APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED: + return "The feature is currently under development. It can be enabled using winget settings."; + case APPINSTALLER_CLI_ERROR_MSSTORE_INSTALL_FAILED: + return "Failed to install the Microsoft Store app"; + case APPINSTALLER_CLI_ERROR_COMPLETE_INPUT_BAD: + return "Failed to perform auto complete"; + case APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED: + return "Failed to initialize YAML parser"; + case APPINSTALLER_CLI_ERROR_YAML_INVALID_MAPPING_KEY: + return "Encountered an invalid YAML key"; + case APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY: + return "Encountered a duplicate YAML key"; + case APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION: + return "Invalid YAML operation"; + case APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED: + return "Failed to build YAML doc"; + case APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE: + return "Invalid YAML emitter state"; + case APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA: + return "Invalid YAML data"; + case APPINSTALLER_CLI_ERROR_LIBYAML_ERROR: + return "LibYAML error"; + case APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_WARNING: + return "Manifest validation succeeded with warning"; + case APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_FAILURE: + return "Manifest validation failed"; + case APPINSTALLER_CLI_ERROR_INVALID_MANIFEST: + return "Manifest is invalid"; + case APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE: + return "No applicable update found"; + case APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE: + return "winget upgrade --all completed with failures"; default: return "Uknown Error Code"; } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h b/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h @@ -3,6 +3,8 @@ #pragma once #include <AppInstallerProgress.h> +#include <urlmon.h> + #include <filesystem> #include <optional> #include <ostream> @@ -39,5 +41,9 @@ namespace AppInstaller::Utility bool IsUrlSecure(std::string_view url); // Apply Mark of the web if the target file is on NTFS, otherwise does nothing. - void ApplyMotwIfApplicable(const std::filesystem::path& filePath); + void ApplyMotwIfApplicable(const std::filesystem::path& filePath, URLZONE zone); + + // Apply Mark of the web using IAttachmentExecute::Save if the target file is on NTFS, otherwise does nothing. + // This method only does a best effort since Attachment Execution Service may be disabled. + void ApplyMotwUsingIAttachmentExecuteIfApplicable(const std::filesystem::path& filePath, const std::string& source); } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h @@ -52,4 +52,7 @@ namespace AppInstaller::Runtime // Determines whether the process is running with administrator privileges. bool IsRunningAsAdmin(); + + // Checks if the file system is NTFS + bool IsNTFS(const std::filesystem::path& filePath); } diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp @@ -389,6 +389,33 @@ namespace AppInstaller::Runtime return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS); } + bool IsNTFS(const std::filesystem::path& filePath) + { + wil::unique_hfile fileHandle{ CreateFileW( + filePath.c_str(), /*lpFileName*/ + FILE_READ_ATTRIBUTES, /*dwDesiredAccess*/ + 0, /*dwShareMode*/ + NULL, /*lpSecurityAttributes*/ + OPEN_EXISTING, /*dwCreationDisposition*/ + FILE_ATTRIBUTE_NORMAL, /*dwFlagsAndAttributes*/ + NULL /*hTemplateFile*/) }; + + THROW_LAST_ERROR_IF(fileHandle.get() == INVALID_HANDLE_VALUE); + + wchar_t fileSystemName[MAX_PATH]; + THROW_LAST_ERROR_IF(!GetVolumeInformationByHandleW( + fileHandle.get(), /*hFile*/ + NULL, /*lpVolumeNameBuffer*/ + 0, /*nVolumeNameSize*/ + NULL, /*lpVolumeSerialNumber*/ + NULL, /*lpMaximumComponentLength*/ + NULL, /*lpFileSystemFlags*/ + fileSystemName, /*lpFileSystemNameBuffer*/ + MAX_PATH /*nFileSystemNameSize*/)); + + return _wcsicmp(fileSystemName, L"NTFS") == 0; + } + #ifndef AICLI_DISABLE_TEST_HOOKS void TestHook_SetPathOverride(PathName target, const std::filesystem::path& path) { diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -145,6 +145,11 @@ namespace AppInstaller::Repository return {}; } + std::shared_ptr<const ISource> GetSource() const override + { + return {}; + } + IPackageVersion::Metadata GetMetadata() const override { return {}; diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -19,7 +19,7 @@ namespace AppInstaller::Repository::Microsoft m_source(source) {} protected: - std::shared_ptr<const SQLiteIndexSource> GetSource() const + std::shared_ptr<const SQLiteIndexSource> GetReferenceSource() const { std::shared_ptr<const SQLiteIndexSource> source = m_source.lock(); THROW_HR_IF(E_NOT_VALID_STATE, !source); @@ -42,12 +42,12 @@ namespace AppInstaller::Repository::Microsoft switch (property) { case PackageVersionProperty::SourceIdentifier: - return LocIndString{ GetSource()->GetIdentifier() }; + return LocIndString{ GetReferenceSource()->GetIdentifier() }; case PackageVersionProperty::SourceName: - return LocIndString{ GetSource()->GetDetails().Name }; + return LocIndString{ GetReferenceSource()->GetDetails().Name }; default: // Values coming from the index will always be localized/independent. - return LocIndString{ GetSource()->GetIndex().GetPropertyByManifestId(m_manifestId, property).value() }; + return LocIndString{ GetReferenceSource()->GetIndex().GetPropertyByManifestId(m_manifestId, property).value() }; } } @@ -55,7 +55,7 @@ namespace AppInstaller::Repository::Microsoft { std::vector<Utility::LocIndString> result; - for (auto&& value : GetSource()->GetIndex().GetMultiPropertyByManifestId(m_manifestId, property)) + for (auto&& value : GetReferenceSource()->GetIndex().GetMultiPropertyByManifestId(m_manifestId, property)) { // Values coming from the index will always be localized/independent. result.emplace_back(std::move(value)); @@ -66,15 +66,20 @@ namespace AppInstaller::Repository::Microsoft Manifest::Manifest GetManifest() const override { - std::shared_ptr<const SQLiteIndexSource> source = GetSource(); + std::shared_ptr<const SQLiteIndexSource> source = GetReferenceSource(); std::optional<std::string> relativePathOpt = source->GetIndex().GetPropertyByManifestId(m_manifestId, PackageVersionProperty::RelativePath); THROW_HR_IF(E_NOT_SET, !relativePathOpt); return GetManifestFromArgAndRelativePath(source->GetDetails().Arg, relativePathOpt.value()); } + std::shared_ptr<const ISource> GetSource() const override + { + return GetReferenceSource(); + } + IPackageVersion::Metadata GetMetadata() const override { - auto metadata = GetSource()->GetIndex().GetMetadataByManifestId(m_manifestId); + auto metadata = GetReferenceSource()->GetIndex().GetMetadataByManifestId(m_manifestId); IPackageVersion::Metadata result; for (auto&& data : metadata) @@ -148,7 +153,7 @@ namespace AppInstaller::Repository::Microsoft protected: std::shared_ptr<IPackageVersion> GetLatestVersionInternal() const { - std::shared_ptr<const SQLiteIndexSource> source = GetSource(); + std::shared_ptr<const SQLiteIndexSource> source = GetReferenceSource(); std::optional<SQLiteIndex::IdType> manifestId = source->GetIndex().GetManifestIdByKey(m_idId, {}, {}); if (manifestId) @@ -180,7 +185,7 @@ namespace AppInstaller::Repository::Microsoft std::vector<PackageVersionKey> GetAvailableVersionKeys() const override { - std::shared_ptr<const SQLiteIndexSource> source = GetSource(); + std::shared_ptr<const SQLiteIndexSource> source = GetReferenceSource(); std::vector<Utility::VersionAndChannel> versions = source->GetIndex().GetVersionKeysById(m_idId); std::vector<PackageVersionKey> result; @@ -198,7 +203,7 @@ namespace AppInstaller::Repository::Microsoft std::shared_ptr<IPackageVersion> GetAvailableVersion(const PackageVersionKey& versionKey) const override { - std::shared_ptr<const SQLiteIndexSource> source = GetSource(); + std::shared_ptr<const SQLiteIndexSource> source = GetReferenceSource(); // Ensure that this key targets this (or any) source if (!versionKey.SourceId.empty() && versionKey.SourceId != source->GetIdentifier()) diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h @@ -16,6 +16,8 @@ namespace AppInstaller::Repository { + struct ISource; + // The type of matching to perform during a search. // The values must be declared in order of preference in search results. enum class MatchType @@ -133,6 +135,9 @@ namespace AppInstaller::Repository // Gets the manifest of this package version. virtual Manifest::Manifest GetManifest() const = 0; + // Gets the source where this package version is from. + virtual std::shared_ptr<const ISource> GetSource() const = 0; + // Gets any metadata associated with this package version. // Primarily stores data on installed packages. virtual Metadata GetMetadata() const = 0; diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h @@ -23,6 +23,13 @@ namespace AppInstaller::Repository Predefined, }; + // Defines the trust level of the source. + enum class SourceTrustLevel + { + None, + Trusted, + }; + std::string_view ToString(SourceOrigin origin); // Interface for retrieving information about a source without acting on it. @@ -45,6 +52,9 @@ namespace AppInstaller::Repository // The origin of the source. SourceOrigin Origin = SourceOrigin::Default; + + // The trust level of the source + SourceTrustLevel TrustLevel = SourceTrustLevel::None; }; // Interface for interacting with a source from outside of the repository lib. diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -193,6 +193,7 @@ namespace AppInstaller::Repository details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); details.Arg = s_Source_WingetCommunityDefault_Arg; details.Data = s_Source_WingetCommunityDefault_Data; + details.TrustLevel = SourceTrustLevel::Trusted; result.emplace_back(std::move(details)); if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::ExperimentalMSStore)) @@ -202,6 +203,7 @@ namespace AppInstaller::Repository storeDetails.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); storeDetails.Arg = s_Source_WingetMSStoreDefault_Arg; storeDetails.Data = s_Source_WingetMSStoreDefault_Data; + storeDetails.TrustLevel = SourceTrustLevel::Trusted; result.emplace_back(std::move(storeDetails)); } }