diff --git a/src/AppInstallerCLICore/Commands/COMInstallCommand.cpp b/src/AppInstallerCLICore/Commands/COMInstallCommand.cpp index 2ebc86d3d4..57a4fc9bc4 100644 --- a/src/AppInstallerCLICore/Commands/COMInstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/COMInstallCommand.cpp @@ -27,8 +27,7 @@ namespace AppInstaller::CLI void COMInstallCommand::ExecuteInternal(Context& context) const { context << - Workflow::GetInstallerHash << - Workflow::VerifyInstallerHash << + Workflow::ReverifyInstallerHash << Workflow::InstallPackageInstaller; } } diff --git a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp index c8b4c61c17..7da993e57a 100644 --- a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp @@ -463,7 +463,7 @@ namespace AppInstaller::CLI::Workflow } } - void GetInstallerHash(Execution::Context& context) + void ReverifyInstallerHash(Execution::Context& context) { const auto& installer = context.Get().value(); @@ -491,6 +491,8 @@ namespace AppInstaller::CLI::Workflow AICLI_LOG(CLI, Error, << "Installer file not found."); AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); } + + context << VerifyInstallerHash; } void RenameDownloadedInstaller(Execution::Context& context) diff --git a/src/AppInstallerCLICore/Workflows/DownloadFlow.h b/src/AppInstallerCLICore/Workflows/DownloadFlow.h index 744de69467..7419ca4655 100644 --- a/src/AppInstallerCLICore/Workflows/DownloadFlow.h +++ b/src/AppInstallerCLICore/Workflows/DownloadFlow.h @@ -35,12 +35,11 @@ namespace AppInstaller::CLI::Workflow // Outputs: HashPair void GetMsixSignatureHash(Execution::Context& context); - // Gets the hash of the downloaded installer. - // Downloading already computes the hash, so this is only needed to re-verify the installer hash. + // Re-verify the installer hash. This is used in Com install commands where download and install are in separate phases. // Required Args: None // Inputs: InstallerPath, Installer // Outputs: HashPair - void GetInstallerHash(Execution::Context& context); + void ReverifyInstallerHash(Execution::Context& context); // Verifies that the downloaded installer hash matches the hash in the manifest. // Required Args: None diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index c2ee32a362..19ec2eddcc 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -142,6 +142,12 @@ public class ErrorCode public const int ERROR_MISSING_RESOURCE_FILE = unchecked((int)0x8A150048); public const int ERROR_MSI_INSTALL_FAILED = unchecked((int)0x8A150049); public const int ERROR_INVALID_MSIEXEC_ARGUMENT = unchecked((int)0x8A15004A); + public const int ERROR_FAILED_TO_OPEN_ALL_SOURCES = unchecked((int)0x8A15004B); + public const int ERROR_DEPENDENCIES_VALIDATION_FAILED = unchecked((int)0x8A15004C); + public const int ERROR_MISSING_PACKAGE = unchecked((int)0x8A15004D); + public const int ERROR_INVALID_TABLE_COLUMN = unchecked((int)0x8A15004E); + public const int ERROR_UPGRADE_VERSION_NOT_NEWER = unchecked((int)0x8A15004F); + public const int ERROR_UPGRADE_VERSION_UNKNOWN = unchecked((int)0x8A150050); public const int ERROR_INSTALL_PACKAGE_IN_USE = unchecked((int)0x8A150101); public const int ERROR_INSTALL_INSTALL_IN_PROGRESS = unchecked((int)0x8A150102); diff --git a/src/AppInstallerCommonCore/Errors.cpp b/src/AppInstallerCommonCore/Errors.cpp index 3c044113a2..c65027b980 100644 --- a/src/AppInstallerCommonCore/Errors.cpp +++ b/src/AppInstallerCommonCore/Errors.cpp @@ -146,8 +146,6 @@ namespace AppInstaller return "Package agreements were not agreed to"; case APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR: return "Error reading input in prompt"; - case APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT: - return "Arguments for msiexec are invalid"; case APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST: return "The search request is not supported by one or more sources"; case APPINSTALLER_CLI_ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND: @@ -160,6 +158,20 @@ namespace AppInstaller return "Header size exceeds the allowable limit of 1024 characters. Please reduce the size and try again."; case APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED: return "Running MSI install failed"; + case APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT: + return "Arguments for msiexec are invalid"; + case APPINSTALLER_CLI_ERROR_FAILED_TO_OPEN_ALL_SOURCES: + return "Failed to open one or more sources"; + case APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED: + return "Failed to validate dependencies"; + case APPINSTALLER_CLI_ERROR_MISSING_PACKAGE: + return "One or more package is missing"; + case APPINSTALLER_CLI_ERROR_INVALID_TABLE_COLUMN: + return "Invalid table column"; + case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER: + return "The upgrade version is not newer than the installed version"; + case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN: + return "Upgrade version is unknown and override is not specified"; case APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE: return "Application is currently running.Exit the application then try again."; case APPINSTALLER_CLI_ERROR_INSTALL_INSTALL_IN_PROGRESS: diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h index 85b449bdb6..a87207f743 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -88,12 +88,13 @@ #define APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED ((HRESULT)0x8A150049) #define APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT ((HRESULT)0x8A15004A) #define APPINSTALLER_CLI_ERROR_FAILED_TO_OPEN_ALL_SOURCES ((HRESULT)0x8A15004B) - -// Error associated with dependencies operations. -#define APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED ((HRESULT)0x8A15004C) +#define APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED ((HRESULT)0x8A15004C) #define APPINSTALLER_CLI_ERROR_MISSING_PACKAGE ((HRESULT)0x8A15004D) -#define APPINSTALLER_CLI_ERROR_INVALID_TABLE_COLUMN ((HRESULT)0x8A15004E) +#define APPINSTALLER_CLI_ERROR_INVALID_TABLE_COLUMN ((HRESULT)0x8A15004E) +#define APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER ((HRESULT)0x8A15004F) +#define APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN ((HRESULT)0x8A150050) +// Install errors. #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101) #define APPINSTALLER_CLI_ERROR_INSTALL_INSTALL_IN_PROGRESS ((HRESULT)0x8A150102) #define APPINSTALLER_CLI_ERROR_INSTALL_FILE_IN_USE ((HRESULT)0x8A150103) diff --git a/src/Microsoft.Management.Deployment.Client/InstallOptions.cpp b/src/Microsoft.Management.Deployment.Client/InstallOptions.cpp index e0d18b843c..ebef6fb126 100644 --- a/src/Microsoft.Management.Deployment.Client/InstallOptions.cpp +++ b/src/Microsoft.Management.Deployment.Client/InstallOptions.cpp @@ -90,4 +90,12 @@ namespace winrt::Microsoft::Management::Deployment::implementation { throw hresult_not_implemented(); } + bool InstallOptions::AllowUpgradeToUnknownVersion() + { + throw hresult_not_implemented(); + } + void InstallOptions::AllowUpgradeToUnknownVersion(bool) + { + throw hresult_not_implemented(); + } } diff --git a/src/Microsoft.Management.Deployment.Client/PackageManager.cpp b/src/Microsoft.Management.Deployment.Client/PackageManager.cpp index 84ab4553de..53a1a21ff0 100644 --- a/src/Microsoft.Management.Deployment.Client/PackageManager.cpp +++ b/src/Microsoft.Management.Deployment.Client/PackageManager.cpp @@ -38,5 +38,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation { throw hresult_not_implemented(); } - + winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::UpgradePackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions) + { + throw hresult_not_implemented(); + } } diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.h b/src/Microsoft.Management.Deployment/CatalogPackage.h index 7dc0f419ec..a245b58e84 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.h +++ b/src/Microsoft.Management.Deployment/CatalogPackage.h @@ -22,7 +22,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation winrt::Microsoft::Management::Deployment::PackageVersionInfo DefaultInstallVersion(); winrt::Microsoft::Management::Deployment::PackageVersionInfo GetPackageVersionInfo(winrt::Microsoft::Management::Deployment::PackageVersionId const& versionKey); bool IsUpdateAvailable(); - + #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Repository::Source m_source; diff --git a/src/Microsoft.Management.Deployment/Converters.cpp b/src/Microsoft.Management.Deployment/Converters.cpp index 69f11a1683..29478b22f8 100644 --- a/src/Microsoft.Management.Deployment/Converters.cpp +++ b/src/Microsoft.Management.Deployment/Converters.cpp @@ -198,6 +198,11 @@ namespace winrt::Microsoft::Management::Deployment::implementation case APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER: resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::NoApplicableInstallers; break; + case APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE: + case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN: + case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::NoApplicableUpgrade; + break; case APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX: case APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED: case APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED: diff --git a/src/Microsoft.Management.Deployment/InstallOptions.cpp b/src/Microsoft.Management.Deployment/InstallOptions.cpp index 07814af6cc..b0599b318a 100644 --- a/src/Microsoft.Management.Deployment/InstallOptions.cpp +++ b/src/Microsoft.Management.Deployment/InstallOptions.cpp @@ -104,6 +104,14 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return m_allowedArchitectures; } + bool InstallOptions::AllowUpgradeToUnknownVersion() + { + return m_allowUpgradeToUnknownVersion; + } + void InstallOptions::AllowUpgradeToUnknownVersion(bool value) + { + m_allowUpgradeToUnknownVersion = value; + } CoCreatableMicrosoftManagementDeploymentClass(InstallOptions); } diff --git a/src/Microsoft.Management.Deployment/InstallOptions.h b/src/Microsoft.Management.Deployment/InstallOptions.h index 6e29523907..e95fab3813 100644 --- a/src/Microsoft.Management.Deployment/InstallOptions.h +++ b/src/Microsoft.Management.Deployment/InstallOptions.h @@ -33,6 +33,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation hstring AdditionalPackageCatalogArguments(); void AdditionalPackageCatalogArguments(hstring const& value); winrt::Windows::Foundation::Collections::IVector AllowedArchitectures(); + bool AllowUpgradeToUnknownVersion(); + void AllowUpgradeToUnknownVersion(bool value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: @@ -47,6 +49,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::wstring m_additionalPackageCatalogArguments = L""; Windows::Foundation::Collections::IVector m_allowedArchitectures{ winrt::single_threaded_vector() }; + bool m_allowUpgradeToUnknownVersion = false; #endif }; } diff --git a/src/Microsoft.Management.Deployment/PackageManager.cpp b/src/Microsoft.Management.Deployment/PackageManager.cpp index 2dee2ca317..93a07702c0 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.cpp +++ b/src/Microsoft.Management.Deployment/PackageManager.cpp @@ -402,7 +402,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::shared_ptr queueItemParam, winrt::Microsoft::Management::Deployment::CatalogPackage package = nullptr, winrt::Microsoft::Management::Deployment::InstallOptions options = nullptr, - std::wstring callerProcessInfoString = {}) + std::wstring callerProcessInfoString = {}, + bool isUpgrade = false) { winrt::hresult terminationHR = S_OK; hstring correlationData = (options) ? options.CorrelationData() : L""; @@ -422,6 +423,34 @@ namespace winrt::Microsoft::Management::Deployment::implementation { Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo = GetPackageVersionInfo(package, options); std::unique_ptr comContext = CreateContextFromInstallOptions(package, options, callerProcessInfoString); + + if (isUpgrade) + { + AppInstaller::Utility::VersionAndChannel installedVersion{ winrt::to_string(package.InstalledVersion().Version()), winrt::to_string(package.InstalledVersion().Channel()) }; + AppInstaller::Utility::VersionAndChannel upgradeVersion{ winrt::to_string(packageVersionInfo.Version()), winrt::to_string(packageVersionInfo.Channel()) }; + + // Perform upgrade version check + if (upgradeVersion.GetVersion().IsUnknown()) + { + if (!(options.AllowUpgradeToUnknownVersion() && + AppInstaller::Utility::ICUCaseInsensitiveEquals(installedVersion.GetChannel().ToString(), upgradeVersion.GetChannel().ToString()))) + { + co_return GetInstallResult(executionStage, APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN, correlationData, false); + } + } + else if (!installedVersion.IsUpdatedBy(upgradeVersion)) + { + co_return GetInstallResult(executionStage, APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER, correlationData, false); + } + + // Set upgrade flag + comContext->SetFlags(AppInstaller::CLI::Execution::ContextFlag::InstallerExecutionUseUpdate); + // Add installed version + winrt::Microsoft::Management::Deployment::implementation::PackageVersionInfo* installedVersionInfoImpl = get_self(package.InstalledVersion()); + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> internalInstalledVersion = installedVersionInfoImpl->GetRepositoryPackageVersion(); + comContext->Add(internalInstalledVersion); + } + queueItem = Execution::OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring{ package.Id() }, std::wstring{ packageVersionInfo.PackageCatalog().Info().Id() }, std::move(comContext)); Execution::ContextOrchestrator::Instance().EnqueueAndRunItem(queueItem); @@ -552,6 +581,32 @@ namespace winrt::Microsoft::Management::Deployment::implementation return GetInstallOperation(true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); } + winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::UpgradePackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options) + { + hstring correlationData = (options) ? options.CorrelationData() : L""; + + // options and catalog can both be null, package must be set. + WINGET_RETURN_INSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); + // the package should have an installed version to be upgraded. + WINGET_RETURN_INSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package.InstalledVersion()); + + HRESULT hr = S_OK; + std::wstring callerProcessInfoString; + try + { + // Check for permissions and get caller info for telemetry. + // This must be done before any co_awaits since it requires info from the rpc caller thread. + auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); + WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hrGetCallerId); + WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId)); + callerProcessInfoString = TryGetCallerProcessInfo(callerProcessId); + } + WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); + WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr); + + return GetInstallOperation(true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString), true /* isUpgrade */); + } + winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::GetInstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo) { hstring correlationData; diff --git a/src/Microsoft.Management.Deployment/PackageManager.h b/src/Microsoft.Management.Deployment/PackageManager.h index 23a28235c1..879fb0a7a5 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.h +++ b/src/Microsoft.Management.Deployment/PackageManager.h @@ -21,9 +21,12 @@ namespace winrt::Microsoft::Management::Deployment::implementation winrt::Microsoft::Management::Deployment::PackageCatalogReference CreateCompositePackageCatalog(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions const& options); winrt::Windows::Foundation::IAsyncOperationWithProgress InstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options); - //Contract 2.0 + // Contract 2.0 winrt::Windows::Foundation::IAsyncOperationWithProgress GetInstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo); + // Contract 4.0 + winrt::Windows::Foundation::IAsyncOperationWithProgress + UpgradePackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options); }; } diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index c20d1ca441..60f548f55c 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -2,7 +2,7 @@ // Licensed under the MIT License. namespace Microsoft.Management.Deployment { - [contractversion(3)] + [contractversion(4)] apicontract WindowsPackageManagerContract{}; /// State of the install. @@ -59,6 +59,10 @@ namespace Microsoft.Management.Deployment InstallError, ManifestError, NoApplicableInstallers, + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] + { + NoApplicableUpgrade, + } }; /// Result of the install @@ -525,6 +529,12 @@ namespace Microsoft.Management.Deployment // architecture after the first will simply be ignored. Windows.Foundation.Collections.IVector AllowedArchitectures { get; }; } + + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] + { + /// Allow the upgrade to continue for upgrade packages with manifest versions Unknown. + Boolean AllowUpgradeToUnknownVersion; + } } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] @@ -554,6 +564,12 @@ namespace Microsoft.Management.Deployment /// Get install progress Windows.Foundation.IAsyncOperationWithProgress GetInstallProgress(CatalogPackage package, PackageCatalogInfo catalogInfo); } + + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] + { + /// Upgrade the specified package + Windows.Foundation.IAsyncOperationWithProgress UpgradePackageAsync(CatalogPackage package, InstallOptions options); + } } /// Force midl3 to generate vector marshalling info.