PackageCatalogReference.cpp (16430B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include <winget/RepositorySource.h> 5 #include "PackageCatalogReference.h" 6 #include "PackageCatalogReference.g.cpp" 7 #include "PackageCatalogInfo.h" 8 #include "PackageCatalog.h" 9 #include "SourceAgreement.h" 10 #include "ConnectResult.h" 11 #include "AuthenticationInfo.h" 12 #include "Workflows/WorkflowBase.h" 13 #include "Converters.h" 14 #include "Microsoft/PredefinedInstalledSourceFactory.h" 15 #include <wil\cppwinrt_wrl.h> 16 #include <winget/GroupPolicy.h> 17 #include <AppInstallerErrors.h> 18 #include <AppInstallerStrings.h> 19 #include <winget/UserSettings.h> 20 #include <Helpers.h> 21 #include <ExecutionContext.h> 22 #include <RefreshPackageCatalogResult.h> 23 #include <PackageCatalogProgress.h> 24 25 namespace winrt::Microsoft::Management::Deployment::implementation 26 { 27 namespace 28 { 29 winrt::Microsoft::Management::Deployment::RefreshPackageCatalogResult GetRefreshPackageCatalogResult(winrt::hresult terminationStatus) 30 { 31 winrt::Microsoft::Management::Deployment::RefreshPackageCatalogStatus status = GetPackageCatalogOperationStatus<RefreshPackageCatalogStatus>(terminationStatus); 32 auto updateResult = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::RefreshPackageCatalogResult>>(); 33 updateResult->Initialize(status, terminationStatus); 34 return *updateResult; 35 } 36 } 37 38 void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::PackageCatalogInfo packageCatalogInfo, ::AppInstaller::Repository::Source sourceReference) 39 { 40 m_info = packageCatalogInfo; 41 m_sourceReference = std::move(sourceReference); 42 m_packageCatalogBackgroundUpdateInterval = ::AppInstaller::Settings::User().Get<::AppInstaller::Settings::Setting::AutoUpdateTimeInMinutes>(); 43 44 if (IsBackgroundProcessForPolicy()) 45 { 46 // Delay the default update interval for these background processes 47 static constexpr winrt::Windows::Foundation::TimeSpan s_PackageCatalogUpdateIntervalDelay_Base = 168h; //1 week 48 49 // Add a bit of randomness to the default interval time 50 std::default_random_engine randomEngine(std::random_device{}()); 51 std::uniform_int_distribution<long long> distribution(0, 604800); 52 53 m_packageCatalogBackgroundUpdateInterval = s_PackageCatalogUpdateIntervalDelay_Base + std::chrono::seconds(distribution(randomEngine)); 54 55 // Prevent any update / data processing by default for these background processes for now 56 m_installedPackageInformationOnly = m_sourceReference.IsWellKnownSource(AppInstaller::Repository::WellKnownSource::WinGet); 57 } 58 } 59 60 void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions options) 61 { 62 m_compositePackageCatalogOptions = options; 63 } 64 65 bool PackageCatalogReference::IsComposite() 66 { 67 return (m_compositePackageCatalogOptions != nullptr); 68 } 69 winrt::Microsoft::Management::Deployment::PackageCatalogInfo PackageCatalogReference::Info() 70 { 71 return m_info; 72 } 73 winrt::Windows::Foundation::IAsyncOperation<winrt::Microsoft::Management::Deployment::ConnectResult> PackageCatalogReference::ConnectAsync() 74 { 75 co_return Connect(); 76 } 77 winrt::Microsoft::Management::Deployment::ConnectResult GetConnectCatalogErrorResult(hresult hr) 78 { 79 auto connectResult = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::ConnectResult>>(); 80 connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::CatalogError, nullptr, hr); 81 return *connectResult; 82 } 83 winrt::Microsoft::Management::Deployment::ConnectResult GetConnectSourceAgreementsNotAcceptedErrorResult() 84 { 85 auto connectResult = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::ConnectResult>>(); 86 connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::SourceAgreementsNotAccepted, nullptr, APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED); 87 return *connectResult; 88 } 89 winrt::Microsoft::Management::Deployment::ConnectResult PackageCatalogReference::Connect() try 90 { 91 HRESULT hr = EnsureComCallerHasCapability(Capability::PackageQuery); 92 if (FAILED(hr)) 93 { 94 // TODO: When more error codes are added, this should go back as something other than CatalogError. 95 return GetConnectCatalogErrorResult(hr); 96 } 97 98 std::string callerName = GetCallerName(); 99 100 ::AppInstaller::ProgressCallback progress; 101 ::AppInstaller::Repository::Source source; 102 if (m_compositePackageCatalogOptions) 103 { 104 std::vector<::AppInstaller::Repository::Source> remoteSources; 105 106 for (uint32_t i = 0; i < m_compositePackageCatalogOptions.Catalogs().Size(); ++i) 107 { 108 auto catalog = m_compositePackageCatalogOptions.Catalogs().GetAt(i); 109 if (!catalog.AcceptSourceAgreements() && catalog.SourceAgreements().Size() != 0) 110 { 111 return GetConnectSourceAgreementsNotAcceptedErrorResult(); 112 } 113 114 winrt::Microsoft::Management::Deployment::implementation::PackageCatalogReference* catalogImpl = get_self<winrt::Microsoft::Management::Deployment::implementation::PackageCatalogReference>(catalog); 115 auto copy = catalogImpl->m_sourceReference; 116 copy.SetCaller(callerName); 117 copy.SetBackgroundUpdateInterval(catalog.PackageCatalogBackgroundUpdateInterval()); 118 copy.InstalledPackageInformationOnly(catalog.InstalledPackageInformationOnly()); 119 if (catalog.AuthenticationInfo().AuthenticationType() != winrt::Microsoft::Management::Deployment::AuthenticationType::None) 120 { 121 copy.SetAuthenticationArguments(GetAuthenticationArguments(catalog.AuthenticationArguments())); 122 } 123 copy.Open(progress); 124 remoteSources.emplace_back(std::move(copy)); 125 } 126 127 // Create the aggregated source. 128 source = ::AppInstaller::Repository::Source{ remoteSources }; 129 130 // Create composite with installed source if needed. 131 ::AppInstaller::Repository::CompositeSearchBehavior searchBehavior = GetRepositoryCompositeSearchBehavior(m_compositePackageCatalogOptions.CompositeSearchBehavior()); 132 133 // Check if search behavior indicates that the caller does not want to do local correlation. 134 if (m_compositePackageCatalogOptions.CompositeSearchBehavior() != Microsoft::Management::Deployment::CompositeSearchBehavior::RemotePackagesFromRemoteCatalogs) 135 { 136 ::AppInstaller::Repository::Source installedSource; 137 auto manifestInstalledScope = GetManifestScope(m_compositePackageCatalogOptions.InstalledScope()).first; 138 if (manifestInstalledScope == ::AppInstaller::Manifest::ScopeEnum::User) 139 { 140 installedSource = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::PredefinedSource::InstalledUser }; 141 } 142 else if (manifestInstalledScope == ::AppInstaller::Manifest::ScopeEnum::Machine) 143 { 144 installedSource = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::PredefinedSource::InstalledMachine }; 145 } 146 else 147 { 148 installedSource = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::PredefinedSource::Installed }; 149 } 150 151 installedSource.Open(progress); 152 source = ::AppInstaller::Repository::Source{ installedSource, source, searchBehavior }; 153 } 154 } 155 else 156 { 157 if (!AcceptSourceAgreements() && SourceAgreements().Size() != 0) 158 { 159 return GetConnectSourceAgreementsNotAcceptedErrorResult(); 160 } 161 162 source = m_sourceReference; 163 source.SetCaller(callerName); 164 source.SetBackgroundUpdateInterval(PackageCatalogBackgroundUpdateInterval()); 165 source.InstalledPackageInformationOnly(m_installedPackageInformationOnly); 166 if (AuthenticationInfo().AuthenticationType() != winrt::Microsoft::Management::Deployment::AuthenticationType::None) 167 { 168 source.SetAuthenticationArguments(GetAuthenticationArguments(m_authenticationArguments)); 169 } 170 source.Open(progress); 171 } 172 173 if (!source) 174 { 175 // We call `Open` on each individual source above, meaning that they should throw any error that occurs. 176 // If the source is still not open at this point it is a bug. 177 return GetConnectCatalogErrorResult(E_UNEXPECTED); 178 } 179 180 // Have to make another package catalog info because source->GetDetails has more fields than m_info does. 181 // Specifically, Rest sources do not have the Ids filled in m_info since they only get the id from the rest server after being Opened. 182 auto packageCatalogInfo = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::PackageCatalogInfo>>(); 183 packageCatalogInfo->Initialize(source.GetDetails()); 184 auto connectResult = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::ConnectResult>>(); 185 auto packageCatalog = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::PackageCatalog>>(); 186 packageCatalog->Initialize(*packageCatalogInfo, source, (m_compositePackageCatalogOptions != nullptr)); 187 connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::Ok, *packageCatalog, S_OK); 188 return *connectResult; 189 } 190 catch (...) 191 { 192 return GetConnectCatalogErrorResult(AppInstaller::CLI::Workflow::HandleException(nullptr, std::current_exception())); 193 } 194 195 winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::SourceAgreement> PackageCatalogReference::SourceAgreements() 196 { 197 std::call_once(m_sourceAgreementsOnceFlag, 198 [&]() 199 { 200 if (!IsComposite()) 201 { 202 for (auto const& agreement : m_sourceReference.GetInformation().SourceAgreements) 203 { 204 auto sourceAgreement = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::SourceAgreement>>(); 205 sourceAgreement->Initialize(agreement); 206 m_sourceAgreements.Append(*sourceAgreement); 207 } 208 } 209 }); 210 return m_sourceAgreements.GetView(); 211 } 212 hstring PackageCatalogReference::AdditionalPackageCatalogArguments() 213 { 214 if (!IsComposite()) 215 { 216 if (m_additionalPackageCatalogArguments.has_value()) 217 { 218 return winrt::to_hstring(m_additionalPackageCatalogArguments.value()); 219 } 220 } 221 222 return {}; 223 } 224 void PackageCatalogReference::AdditionalPackageCatalogArguments(hstring const& value) 225 { 226 if (IsComposite()) 227 { 228 // Can't set AdditionalPackageCatalogArguments on a composite. Callers should set it on each non-composite PackageCatalogReference in the composite. 229 throw winrt::hresult_illegal_state_change(); 230 } 231 else 232 { 233 m_additionalPackageCatalogArguments = ::AppInstaller::Utility::ConvertToUTF8(value); 234 m_sourceReference.SetCustomHeader(m_additionalPackageCatalogArguments); 235 } 236 } 237 void PackageCatalogReference::AcceptSourceAgreements(bool value) 238 { 239 if (IsComposite()) 240 { 241 // Can't set AcceptSourceAgreements on a composite. Callers should set it on each non-composite PackageCatalogReference in the composite. 242 throw winrt::hresult_illegal_state_change(); 243 } 244 m_acceptSourceAgreements = value; 245 } 246 bool PackageCatalogReference::AcceptSourceAgreements() 247 { 248 return m_acceptSourceAgreements; 249 } 250 251 void PackageCatalogReference::PackageCatalogBackgroundUpdateInterval(winrt::Windows::Foundation::TimeSpan const& value) 252 { 253 if (IsComposite()) 254 { 255 // Can't set PackageCatalogBackgroundUpdateInterval on a composite. Callers should set it on each non-composite PackageCatalogReference in the composite. 256 throw winrt::hresult_illegal_state_change(); 257 } 258 m_packageCatalogBackgroundUpdateInterval = value; 259 } 260 winrt::Windows::Foundation::TimeSpan PackageCatalogReference::PackageCatalogBackgroundUpdateInterval() 261 { 262 return m_packageCatalogBackgroundUpdateInterval; 263 } 264 265 bool PackageCatalogReference::InstalledPackageInformationOnly() 266 { 267 return m_installedPackageInformationOnly; 268 } 269 270 void PackageCatalogReference::InstalledPackageInformationOnly(bool value) 271 { 272 if (IsComposite()) 273 { 274 throw winrt::hresult_illegal_state_change(); 275 } 276 277 m_installedPackageInformationOnly = value; 278 } 279 winrt::Microsoft::Management::Deployment::AuthenticationArguments PackageCatalogReference::AuthenticationArguments() 280 { 281 return m_authenticationArguments; 282 } 283 void PackageCatalogReference::AuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments const& value) 284 { 285 if (IsComposite()) 286 { 287 throw winrt::hresult_illegal_state_change(); 288 } 289 290 m_authenticationArguments = value; 291 } 292 winrt::Microsoft::Management::Deployment::AuthenticationInfo PackageCatalogReference::AuthenticationInfo() 293 { 294 std::call_once(m_authenticationInfoOnceFlag, 295 [&]() 296 { 297 if (!IsComposite()) 298 { 299 auto authenticationInfo = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::AuthenticationInfo>>(); 300 authenticationInfo->Initialize(m_sourceReference.GetInformation().Authentication); 301 m_authenticationInfo = *authenticationInfo; 302 } 303 }); 304 return m_authenticationInfo; 305 } 306 307 winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::RefreshPackageCatalogResult, double> PackageCatalogReference::RefreshPackageCatalogAsync() 308 { 309 HRESULT terminationHR = S_OK; 310 try { 311 // Check for permissions and get caller info for telemetry 312 THROW_IF_FAILED(EnsureComCallerHasCapability(Capability::PackageQuery)); 313 314 auto strong_this = get_strong(); 315 auto report_progress{ co_await winrt::get_progress_token() }; 316 co_await winrt::resume_background(); 317 318 auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink(this->m_sourceReference.GetDetails().Type, report_progress); 319 320 packageCatalogProgressSink->BeginProgress(); 321 ::AppInstaller::ProgressCallback progress(packageCatalogProgressSink.get()); 322 this->m_sourceReference.Update(progress); 323 packageCatalogProgressSink->EndProgress(false); 324 } 325 catch (...) 326 { 327 terminationHR = AppInstaller::CLI::Workflow::HandleException(nullptr, std::current_exception()); 328 } 329 330 co_return GetRefreshPackageCatalogResult(terminationHR); 331 } 332 }