winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

MSStoreDownload.cpp (53067B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include <AppInstallerStrings.h>
      5 #include <AppInstallerErrors.h>
      6 #include <AppinstallerLogging.h>
      7 #include "AppInstallerMsixInfo.h"
      8 #include "AppInstallerRuntime.h"
      9 #include "winget/HttpClientHelper.h"
     10 #include "winget/JsonUtil.h"
     11 #include "winget/Locale.h"
     12 #include "winget/MSStoreDownload.h"
     13 #include "winget/NetworkSettings.h"
     14 #include "winget/Rest.h"
     15 #include "winget/UserSettings.h"
     16 #ifndef WINGET_DISABLE_FOR_FUZZING
     17 #include <sfsclient/SFSClient.h>
     18 #endif
     19 
     20 namespace AppInstaller::MSStore
     21 {
     22     using namespace std::string_view_literals;
     23 
     24 #ifndef AICLI_DISABLE_TEST_HOOKS
     25     namespace TestHooks
     26     {
     27         static std::shared_ptr<web::http::http_pipeline_stage> s_DisplayCatalog_HttpPipelineStage_Override = nullptr;
     28 
     29         void SetDisplayCatalogHttpPipelineStage_Override(std::shared_ptr<web::http::http_pipeline_stage> value)
     30         {
     31             s_DisplayCatalog_HttpPipelineStage_Override = value;
     32         }
     33 
     34         static std::function<std::vector<SFS::AppContent>(std::string_view)>* s_SfsClient_AppContents_Override = nullptr;
     35 
     36         void SetSfsClientAppContents_Override(std::function<std::vector<SFS::AppContent>(std::string_view)>* value)
     37         {
     38             s_SfsClient_AppContents_Override = value;
     39         }
     40 
     41         static std::shared_ptr<web::http::http_pipeline_stage> s_Licensing_HttpPipelineStage_Override = nullptr;
     42 
     43         void SetLicensingHttpPipelineStage_Override(std::shared_ptr<web::http::http_pipeline_stage> value)
     44         {
     45             s_Licensing_HttpPipelineStage_Override = value;
     46         }
     47     }
     48 #endif
     49 
     50     namespace DisplayCatalogDetails
     51     {
     52         // Default preferred sku to use
     53         constexpr std::string_view TargetSkuIdValue = "0015"sv;
     54 
     55         // Json response fields
     56         constexpr std::string_view Product = "Product"sv;
     57         constexpr std::string_view DisplaySkuAvailabilities = "DisplaySkuAvailabilities"sv;
     58         constexpr std::string_view Sku = "Sku"sv;
     59         constexpr std::string_view SkuId = "SkuId"sv;
     60         constexpr std::string_view Properties = "Properties"sv;
     61         constexpr std::string_view Packages = "Packages"sv;
     62         constexpr std::string_view Languages = "Languages"sv;
     63         constexpr std::string_view PackageFormat = "PackageFormat"sv;
     64         constexpr std::string_view PackageId = "PackageId"sv;
     65         constexpr std::string_view Architectures = "Architectures"sv;
     66         constexpr std::string_view ContentId = "ContentId"sv;
     67         constexpr std::string_view FulfillmentData = "FulfillmentData"sv;
     68         constexpr std::string_view WuCategoryId = "WuCategoryId"sv;
     69 
     70         // Display catalog rest endpoint
     71         constexpr std::string_view DisplayCatalogRestApi = R"(https://displaycatalog.mp.microsoft.com/v7.0/products/{0}?fieldsTemplate={1}&market={2}&languages={3}&catalogIds={4})";
     72         constexpr std::string_view Details = "Details"sv;
     73         constexpr std::string_view Neutral = "Neutral"sv;
     74         constexpr std::string_view TargetCatalogId = "4"sv;
     75 
     76         enum class DisplayCatalogPackageFormatEnum
     77         {
     78             Unknown,
     79             AppxBundle,
     80             MsixBundle,
     81             Appx,
     82             Msix,
     83         };
     84 
     85         DisplayCatalogPackageFormatEnum ConvertToPackageFormatEnum(std::string_view packageFormatStr)
     86         {
     87             std::string packageFormat = Utility::ToLower(packageFormatStr);
     88             if (packageFormat == "appxbundle")
     89             {
     90                 return DisplayCatalogPackageFormatEnum::AppxBundle;
     91             }
     92             else if (packageFormat == "msixbundle")
     93             {
     94                 return DisplayCatalogPackageFormatEnum::MsixBundle;
     95             }
     96             else if (packageFormat == "appx")
     97             {
     98                 return DisplayCatalogPackageFormatEnum::Appx;
     99             }
    100             else if (packageFormat == "msix")
    101             {
    102                 return DisplayCatalogPackageFormatEnum::Msix;
    103             }
    104 
    105             AICLI_LOG(Core, Info, << "ConvertToPackageFormatEnum: Unknown package format: " << packageFormatStr);
    106             return DisplayCatalogPackageFormatEnum::Unknown;
    107         }
    108 
    109         struct DisplayCatalogPackage
    110         {
    111             std::string PackageId;
    112 
    113             std::vector<AppInstaller::Utility::Architecture> Architectures;
    114 
    115             std::vector<std::string> Languages;
    116 
    117             DisplayCatalogPackageFormatEnum PackageFormat = DisplayCatalogPackageFormatEnum::Unknown;
    118 
    119             // To be used later in sfs-client
    120             std::string WuCategoryId;
    121 
    122             // To be used later in licensing
    123             std::string ContentId;
    124         };
    125 
    126         // Display catalog package comparison logic.
    127         // The comparator follows similar logic as ManifestComparator.
    128         namespace DisplayCatalogPackageComparison
    129         {
    130             struct DisplayCatalogPackageComparisonField
    131             {
    132                 DisplayCatalogPackageComparisonField(std::string_view name) : m_name(name) {}
    133 
    134                 virtual ~DisplayCatalogPackageComparisonField() = default;
    135 
    136                 std::string_view Name() const { return m_name; }
    137 
    138                 virtual bool IsApplicable(const DisplayCatalogPackage& package) = 0;
    139 
    140                 virtual bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second) = 0;
    141 
    142             private:
    143                 std::string_view m_name;
    144             };
    145 
    146             struct PackageFormatComparator : public DisplayCatalogPackageComparisonField
    147             {
    148                 PackageFormatComparator() : DisplayCatalogPackageComparisonField("Package Format") {}
    149 
    150                 bool IsApplicable(const DisplayCatalogPackage& package) override
    151                 {
    152                     return package.PackageFormat != DisplayCatalogPackageFormatEnum::Unknown;
    153                 }
    154 
    155                 bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second) override
    156                 {
    157                     return IsPackageFormatBundle(first) && !IsPackageFormatBundle(second);
    158                 }
    159 
    160             private:
    161                 bool IsPackageFormatBundle(const DisplayCatalogPackage& package)
    162                 {
    163                     return
    164                         package.PackageFormat == DisplayCatalogPackageFormatEnum::AppxBundle ||
    165                         package.PackageFormat == DisplayCatalogPackageFormatEnum::MsixBundle;
    166                 }
    167             };
    168 
    169             struct LocaleComparator : public DisplayCatalogPackageComparisonField
    170             {
    171                 LocaleComparator(std::string locale) : DisplayCatalogPackageComparisonField("Locale")
    172                 {
    173                     if (!locale.empty())
    174                     {
    175                         m_locales.emplace_back(std::move(locale));
    176                         m_isRequirement = true;
    177                     }
    178                     else
    179                     {
    180                         m_locales = Locale::GetUserPreferredLanguages();
    181                     }
    182 
    183                     AICLI_LOG(Core, Verbose,
    184                         << "Locale Comparator created with locales: " << Utility::ConvertContainerToString(m_locales)
    185                         << " , Is requirement: " << m_isRequirement);
    186                 }
    187 
    188                 bool IsApplicable(const DisplayCatalogPackage& package) override
    189                 {
    190                     if (m_isRequirement)
    191                     {
    192                         for (auto const& locale : m_locales)
    193                         {
    194                             double distanceScore = GetBestDistanceScoreFromList(locale, package.Languages);
    195                             if (distanceScore >= Locale::MinimumDistanceScoreAsCompatibleMatch)
    196                             {
    197                                 return true;
    198                             }
    199                         }
    200 
    201                         return false;
    202                     }
    203                     else
    204                     {
    205                         return true;
    206                     }
    207                 }
    208 
    209                 bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second)
    210                 {
    211                     for (auto const& locale : m_locales)
    212                     {
    213                         double firstScore = GetBestDistanceScoreFromList(locale, first.Languages);
    214                         double secondScore = GetBestDistanceScoreFromList(locale, second.Languages);
    215 
    216                         if (firstScore >= Locale::MinimumDistanceScoreAsCompatibleMatch || secondScore >= Locale::MinimumDistanceScoreAsCompatibleMatch)
    217                         {
    218                             return firstScore > secondScore;
    219                         }
    220                     }
    221 
    222                     return false;
    223                 }
    224 
    225             private:
    226                 double GetBestDistanceScoreFromList(std::string_view targetLocale, const std::vector<std::string>& locales)
    227                 {
    228                     double finalScore = 0;
    229                     for (auto const& locale : locales)
    230                     {
    231                         double currentScore = Locale::GetDistanceOfLanguage(targetLocale, locale);
    232                         if (currentScore > finalScore)
    233                         {
    234                             finalScore = currentScore;
    235                         }
    236                     }
    237 
    238                     return finalScore;
    239                 }
    240 
    241                 std::vector<std::string> m_locales;
    242                 bool m_isRequirement = false;
    243             };
    244 
    245             struct ArchitectureComparator : public DisplayCatalogPackageComparisonField
    246             {
    247                 ArchitectureComparator(Utility::Architecture architecture) : DisplayCatalogPackageComparisonField("Architecture")
    248                 {
    249                     if (architecture != Utility::Architecture::Unknown)
    250                     {
    251                         m_architectures.emplace_back(architecture);
    252                         m_isRequirement = true;
    253                     }
    254                     else
    255                     {
    256                         m_architectures = Utility::GetApplicableArchitectures();
    257                     }
    258 
    259                     AICLI_LOG(Core, Verbose,
    260                         << "Architecture Comparator created with archs: " << Utility::ConvertContainerToString(m_architectures, Utility::ToString)
    261                         << " , Is requirement: " << m_isRequirement);
    262                 }
    263 
    264                 bool IsApplicable(const DisplayCatalogPackage& package) override
    265                 {
    266                     if (m_isRequirement)
    267                     {
    268                         for (auto arch : package.Architectures)
    269                         {
    270                             if (Utility::IsApplicableArchitecture(arch, m_architectures) > Utility::InapplicableArchitecture)
    271                             {
    272                                 return true;
    273                             }
    274                         }
    275 
    276                         return false;
    277                     }
    278                     else
    279                     {
    280                         return true;
    281                     }
    282                 }
    283 
    284                 bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second) override
    285                 {
    286                     for (auto arch : m_architectures)
    287                     {
    288                         auto firstItr = std::find(first.Architectures.begin(), first.Architectures.end(), arch);
    289                         auto secondItr = std::find(second.Architectures.begin(), second.Architectures.end(), arch);
    290 
    291                         if (firstItr != first.Architectures.end() && secondItr == second.Architectures.end())
    292                         {
    293                             true;
    294                         }
    295                         else if (secondItr != second.Architectures.end())
    296                         {
    297                             return false;
    298                         }
    299                     }
    300 
    301                     return false;
    302                 }
    303 
    304             private:
    305                 std::vector<Utility::Architecture> m_architectures;
    306                 bool m_isRequirement = false;
    307             };
    308 
    309             struct DisplayCatalogPackageComparator
    310             {
    311                 DisplayCatalogPackageComparator(std::string requiredLocale, Utility::Architecture requiredArch)
    312                 {
    313                     // Order of comparators matters.
    314                     AddComparator(std::make_unique<LocaleComparator>(requiredLocale));
    315                     AddComparator(std::make_unique<ArchitectureComparator>(requiredArch));
    316                     AddComparator(std::make_unique<PackageFormatComparator>());
    317                 }
    318 
    319                 // Gets the best installer from the manifest, if at least one is applicable.
    320                 std::optional<DisplayCatalogPackage> GetPreferredPackage(const std::vector<DisplayCatalogPackage>& packages)
    321                 {
    322                     AICLI_LOG(Core, Verbose, << "Starting display catalog package selection.");
    323 
    324                     const DisplayCatalogPackage* result = nullptr;
    325                     for (const auto& package : packages)
    326                     {
    327                         if (IsApplicable(package) && (!result || IsFirstBetter(package, *result)))
    328                         {
    329                             result = &package;
    330                         }
    331                     }
    332 
    333                     if (result)
    334                     {
    335                         return *result;
    336                     }
    337                     else
    338                     {
    339                         return {};
    340                     }
    341                 }
    342 
    343                 // Determines if the package is applicable.
    344                 bool IsApplicable(const DisplayCatalogPackage& package)
    345                 {
    346                     for (const auto& comparator : m_comparators)
    347                     {
    348                         if (!comparator->IsApplicable(package))
    349                         {
    350                             return false;
    351                         }
    352                     }
    353 
    354                     return true;
    355                 }
    356 
    357                 // Determines if the first package is a better choice.
    358                 bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second)
    359                 {
    360                     for (const auto& comparator : m_comparators)
    361                     {
    362                         bool forwardCompare = comparator->IsFirstBetter(first, second);
    363                         bool reverseCompare = comparator->IsFirstBetter(second, first);
    364 
    365                         if (forwardCompare && reverseCompare)
    366                         {
    367                             AICLI_LOG(Core, Error, << "Packages are both better than each other?");
    368                             THROW_HR(E_UNEXPECTED);
    369                         }
    370 
    371                         if (forwardCompare && !reverseCompare)
    372                         {
    373                             AICLI_LOG(Core, Verbose, << "Package " << first.PackageId << " is better than " << second.PackageId);
    374                             return true;
    375                         }
    376                     }
    377 
    378                     AICLI_LOG(Core, Verbose, << "Package " << first.PackageId << " is equivalent in priority to " << second.PackageId);
    379                     return false;
    380                 }
    381 
    382             private:
    383                 void AddComparator(std::unique_ptr<DisplayCatalogPackageComparisonField>&& comparator)
    384                 {
    385                     if (comparator)
    386                     {
    387                         m_comparators.emplace_back(std::move(comparator));
    388                     }
    389                 }
    390 
    391                 std::vector<std::unique_ptr<DisplayCatalogPackageComparisonField>> m_comparators;
    392             };
    393         }
    394 
    395         // Display catalog API invocation and handling
    396 
    397         utility::string_t GetDisplayCatalogRestApi(std::string_view productId, std::string_view locale)
    398         {
    399             std::vector<Utility::LocIndString> locales;
    400             if (!locale.empty())
    401             {
    402                 locales.emplace_back(locale);
    403             }
    404             else
    405             {
    406                 for (auto const& localeEntry : Locale::GetUserPreferredLanguages())
    407                 {
    408                     locales.emplace_back(localeEntry);
    409                 }
    410             }
    411 
    412             // Neutral is always added
    413             locales.emplace_back(Neutral);
    414 
    415             auto restEndpoint = AppInstaller::Utility::Format(std::string{ DisplayCatalogRestApi },
    416                 productId, Details, AppInstaller::Runtime::GetOSRegion(), Utility::Join(Utility::LocIndView(","), locales), TargetCatalogId);
    417 
    418             return JSON::GetUtilityString(restEndpoint);
    419         }
    420 
    421         // Response format:
    422         // {
    423         //   "Product": {
    424         //     "DisplaySkuAvailabilities": [
    425         //       {
    426         //         "Sku": {
    427         //           "SkuId": "0015",
    428         //           ... Sku Contents ...
    429         //         }
    430         //       }
    431         //     ]
    432         //   }
    433         // }
    434         std::reference_wrapper<const web::json::value> GetSkuNodeFromDisplayCatalogResponse(const web::json::value& responseObject)
    435         {
    436             AICLI_LOG(Core, Info, << "Started parsing display catalog response. Try to find target sku: " << TargetSkuIdValue);
    437 
    438             if (responseObject.is_null())
    439             {
    440                 AICLI_LOG(Core, Error, << "Missing DisplayCatalog Response json object.");
    441                 THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED);
    442             }
    443 
    444             std::optional<std::reference_wrapper<const web::json::value>> product = JSON::GetJsonValueFromNode(responseObject, JSON::GetUtilityString(Product));
    445             if (!product)
    446             {
    447                 AICLI_LOG(Core, Error, << "Missing Product node");
    448                 THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED);
    449             }
    450 
    451             auto skuEntries = JSON::GetRawJsonArrayFromJsonNode(product.value().get(), JSON::GetUtilityString(DisplaySkuAvailabilities));
    452             if (!skuEntries)
    453             {
    454                 AICLI_LOG(Core, Error, << "Missing DisplaySkuAvailabilities");
    455                 THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED);
    456             }
    457 
    458             for (const auto& skuEntry : skuEntries.value().get())
    459             {
    460                 std::optional<std::reference_wrapper<const web::json::value>> sku = JSON::GetJsonValueFromNode(skuEntry, JSON::GetUtilityString(Sku));
    461                 if (!sku)
    462                 {
    463                     AICLI_LOG(Core, Error, << "Missing Sku");
    464                     THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED);
    465                 }
    466 
    467                 const auto& skuValue = sku.value().get();
    468                 auto skuId = JSON::GetRawStringValueFromJsonNode(skuValue, JSON::GetUtilityString(SkuId)).value_or("");
    469                 if (TargetSkuIdValue == skuId)
    470                 {
    471                     AICLI_LOG(Core, Info, << "Target Sku (" << TargetSkuIdValue << ") found");
    472                     return skuValue;
    473                 }
    474             }
    475 
    476             AICLI_LOG(Core, Error, << "Target Sku (" << TargetSkuIdValue << ") not found");
    477             THROW_HR(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE);
    478         }
    479 
    480         // Response format:
    481         // {
    482         //   "Sku": {
    483         //     "Properties": {
    484         //       "Packages": [
    485         //         {
    486         //           "PackageId": "package id",
    487         //           "Architectures": [ "x86", "x64" ],
    488         //           "Languages": [ "en", "fr" ],
    489         //           "PackageFormat": "AppxBundle",
    490         //           "ContentId": "guid",
    491         //           "FulfillmentData": {
    492         //             "WuCategoryId": "guid",
    493         //           }
    494         //         }
    495         //       ]
    496         //     }
    497         //   }
    498         // }
    499         std::vector<DisplayCatalogPackage> GetDisplayCatalogPackagesFromSkuNode(const web::json::value& jsonObject)
    500         {
    501             AICLI_LOG(Core, Info, << "Started extracting display catalog packages from sku.");
    502 
    503             std::optional<std::reference_wrapper<const web::json::value>> properties = JSON::GetJsonValueFromNode(jsonObject, JSON::GetUtilityString(Properties));
    504             if (!properties)
    505             {
    506                 AICLI_LOG(Core, Error, << "Missing Properties");
    507                 THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED);
    508             }
    509 
    510             const auto& propertiesValue = properties.value().get();
    511             auto packages = JSON::GetRawJsonArrayFromJsonNode(propertiesValue, JSON::GetUtilityString(Packages));
    512             if (!packages)
    513             {
    514                 AICLI_LOG(Core, Error, << "Missing Packages");
    515                 THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED);
    516             }
    517 
    518             std::vector<DisplayCatalogPackage> displayCatalogPackages;
    519 
    520             for (const auto& packageEntry : packages.value().get())
    521             {
    522                 DisplayCatalogPackage catalogPackage;
    523 
    524                 // Package Id
    525                 catalogPackage.PackageId = JSON::GetRawStringValueFromJsonNode(packageEntry, JSON::GetUtilityString(PackageId)).value_or("");
    526                 // Architectures
    527                 auto architectures = JSON::GetRawStringArrayFromJsonNode(packageEntry, JSON::GetUtilityString(Architectures));
    528                 for (const auto& arch : architectures)
    529                 {
    530                     auto archEnum = Utility::ConvertToArchitectureEnum(arch);
    531                     if (archEnum != Utility::Architecture::Unknown)
    532                     {
    533                         catalogPackage.Architectures.emplace_back(archEnum);
    534                     }
    535                 }
    536                 // Languages
    537                 auto languages = JSON::GetRawStringArrayFromJsonNode(packageEntry, JSON::GetUtilityString(Languages));
    538                 for (const auto& language : languages)
    539                 {
    540                     catalogPackage.Languages.emplace_back(language);
    541                 }
    542                 // Package Format
    543                 auto packageFormat = JSON::GetRawStringValueFromJsonNode(packageEntry, JSON::GetUtilityString(PackageFormat)).value_or("");
    544                 catalogPackage.PackageFormat = ConvertToPackageFormatEnum(packageFormat);
    545                 // Content Id
    546                 catalogPackage.ContentId = JSON::GetRawStringValueFromJsonNode(packageEntry, JSON::GetUtilityString(ContentId)).value_or("");
    547                 if (catalogPackage.ContentId.empty())
    548                 {
    549                     AICLI_LOG(Core, Warning, << "Missing ContentId");
    550                     // ContentId is required for licensing. Skip this package if missing.
    551                     continue;
    552                 }
    553                 // WuCategoryId
    554                 std::optional<std::reference_wrapper<const web::json::value>> fulfillmentData = JSON::GetJsonValueFromNode(packageEntry, JSON::GetUtilityString(FulfillmentData));
    555                 if (!fulfillmentData)
    556                 {
    557                     AICLI_LOG(Core, Warning, << "Missing FulfillmentData");
    558                     // WuCategoryId is required for sfs-client. Skip this package if missing.
    559                     continue;
    560                 }
    561                 catalogPackage.WuCategoryId = JSON::GetRawStringValueFromJsonNode(fulfillmentData.value().get(), JSON::GetUtilityString(WuCategoryId)).value_or("");
    562                 if (catalogPackage.WuCategoryId.empty())
    563                 {
    564                     AICLI_LOG(Core, Warning, << "Missing WuCategoryId");
    565                     // WuCategoryId is required for sfs-client. Skip this package if missing.
    566                     continue;
    567                 }
    568 
    569                 displayCatalogPackages.emplace_back(std::move(catalogPackage));
    570             }
    571 
    572             return displayCatalogPackages;
    573         }
    574 
    575         DisplayCatalogPackage CallDisplayCatalogAndGetPreferredPackage(std::string_view productId, std::string_view locale, Utility::Architecture architecture, const Http::HttpClientHelper::HttpRequestHeaders& authHeaders)
    576         {
    577             AICLI_LOG(Core, Info, << "CallDisplayCatalogAndGetPreferredPackage with ProductId: " << productId << " Locale: " << locale << " Architecture: " << Utility::ToString(architecture));
    578 
    579             auto displayCatalogApi = GetDisplayCatalogRestApi(productId, locale);
    580 
    581             AppInstaller::Http::HttpClientHelper httpClientHelper;
    582 
    583 #ifndef AICLI_DISABLE_TEST_HOOKS
    584             if (TestHooks::s_DisplayCatalog_HttpPipelineStage_Override)
    585             {
    586                 httpClientHelper = AppInstaller::Http::HttpClientHelper{ TestHooks::s_DisplayCatalog_HttpPipelineStage_Override };
    587             }
    588 #endif
    589 
    590             std::optional<web::json::value> displayCatalogResponseObject = httpClientHelper.HandleGet(displayCatalogApi, {}, authHeaders);
    591 
    592             if (!displayCatalogResponseObject)
    593             {
    594                 AICLI_LOG(Core, Error, << "No display catalog json object found");
    595                 THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED);
    596             }
    597 
    598             const auto& sku = GetSkuNodeFromDisplayCatalogResponse(displayCatalogResponseObject.value());
    599             auto displayCatalogPackages = GetDisplayCatalogPackagesFromSkuNode(sku.get());
    600 
    601             DisplayCatalogPackageComparison::DisplayCatalogPackageComparator packageComparator{ std::string{ locale }, architecture };
    602             auto preferredPackageResult = packageComparator.GetPreferredPackage(displayCatalogPackages);
    603 
    604             if (!preferredPackageResult)
    605             {
    606                 AICLI_LOG(Core, Error,
    607                     << "No applicable display catalog package found for ProductId: " << productId
    608                     << " , Locale: " << locale << " , Architecture: " << Utility::ToString(architecture));
    609 
    610                 THROW_HR(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE);
    611             }
    612 
    613             auto preferredPackage = preferredPackageResult.value();
    614 
    615             AICLI_LOG(Core, Info,
    616                 << "DisplayCatalog package selected. WuCategoryId: " << preferredPackage.WuCategoryId
    617                 << " , ContentId: " << preferredPackage.ContentId);
    618 
    619             return preferredPackage;
    620         }
    621     }
    622 
    623 #ifndef WINGET_DISABLE_FOR_FUZZING
    624     namespace SfsClientDetails
    625     {
    626         const std::string SupportedFileTypes[] = { ".msix", ".msixbundle", ".appx", ".appxbundle" };
    627 
    628         Manifest::PlatformEnum ConvertFromSfsPlatform(std::string_view applicability)
    629         {
    630             if (Utility::CaseInsensitiveStartsWith(applicability, "universal"))
    631             {
    632                 return Manifest::PlatformEnum::Universal;
    633             }
    634             else if (Utility::CaseInsensitiveStartsWith(applicability, "desktop"))
    635             {
    636                 return Manifest::PlatformEnum::Desktop;
    637             }
    638             else if (Utility::CaseInsensitiveStartsWith(applicability, "iot"))
    639             {
    640                 return Manifest::PlatformEnum::IoT;
    641             }
    642             else if (Utility::CaseInsensitiveStartsWith(applicability, "analog"))
    643             {
    644                 return Manifest::PlatformEnum::Holographic;
    645             }
    646             else if (Utility::CaseInsensitiveStartsWith(applicability, "ppi"))
    647             {
    648                 return Manifest::PlatformEnum::Team;
    649             }
    650 
    651             return Manifest::PlatformEnum::Unknown;
    652         }
    653 
    654         // Parses a string of the form `<PLATFORM>=<MINIMUM REQUIRED VERSION>{,}?`.
    655         struct PlatformApplicability
    656         {
    657             explicit PlatformApplicability(std::string_view input, bool extractVersion = true) :
    658                 Platform(ConvertFromSfsPlatform(input))
    659             {
    660                 if (extractVersion)
    661                 {
    662                     THROW_HR_IF(E_INVALIDARG, input.empty());
    663 
    664                     size_t position = input.find('=');
    665                     THROW_HR_IF(E_INVALIDARG, std::string_view::npos == position);
    666 
    667                     position += 1;
    668                     size_t length = input.size() - position;
    669                     if (length > 0 && input.back() == ',')
    670                     {
    671                         length -= 1;
    672                     }
    673 
    674                     MinimumVersion = Utility::UInt64Version{ std::string{ input.substr(position, length) } };
    675                 }
    676             }
    677 
    678             Manifest::PlatformEnum Platform;
    679             std::optional<Utility::UInt64Version> MinimumVersion;
    680         };
    681 
    682         Utility::Architecture ConvertFromSfsArchitecture(SFS::Architecture sfsArchitecture)
    683         {
    684             switch (sfsArchitecture)
    685             {
    686             case SFS::Architecture::Amd64:
    687                 return Utility::Architecture::X64;
    688             case SFS::Architecture::x86:
    689                 return Utility::Architecture::X86;
    690             case SFS::Architecture::Arm64:
    691                 return Utility::Architecture::Arm64;
    692             case SFS::Architecture::Arm:
    693                 return Utility::Architecture::Arm;
    694             case SFS::Architecture::None:
    695                 return Utility::Architecture::Neutral;
    696             }
    697 
    698             return Utility::Architecture::Unknown;
    699         }
    700 
    701         std::vector<Manifest::PlatformEnum> GetSfsPackageFileSupportedPlatforms(
    702             const SFS::AppFile& appFile,
    703             Manifest::PlatformEnum requiredPlatform,
    704             const std::optional<Utility::UInt64Version>& targetOSVersion)
    705         {
    706             std::vector<Manifest::PlatformEnum> supportedPlatforms;
    707 
    708             for (auto const& applicability : appFile.GetApplicabilityDetails().GetPlatformApplicabilityForPackage())
    709             {
    710                 AICLI_LOG(Core, Verbose, << "  examining platform [" << applicability << "] for applicability...");
    711                 PlatformApplicability platform(applicability, targetOSVersion.has_value());
    712 
    713                 if (platform.Platform == Manifest::PlatformEnum::Unknown)
    714                 {
    715                     AICLI_LOG(Core, Verbose, << "    not applicable due to unknown platform");
    716                     continue;
    717                 }
    718 
    719                 if (platform.MinimumVersion && targetOSVersion)
    720                 {
    721                     if (targetOSVersion.value() < platform.MinimumVersion.value())
    722                     {
    723                         AICLI_LOG(Core, Verbose, << "    not applicable due to OS version; target ["
    724                             << targetOSVersion.value().ToString() << "] is lower than minimum ["
    725                             << platform.MinimumVersion.value().ToString() << "]");
    726                         continue;
    727                     }
    728                 }
    729 
    730                 if (platform.Platform == requiredPlatform || requiredPlatform == Manifest::PlatformEnum::Unknown)
    731                 {
    732                     AICLI_LOG(Core, Verbose, << "    applicable");
    733                     supportedPlatforms.emplace_back(platform.Platform);
    734                 }
    735                 else
    736                 {
    737                     AICLI_LOG(Core, Verbose, << "    not applicable due to platform requirement");
    738                 }
    739             }
    740 
    741             return supportedPlatforms;
    742         }
    743 
    744         std::vector<Utility::Architecture> GetSfsPackageFileSupportedArchitectures(const SFS::AppFile& appFile, Utility::Architecture requiredArchitecture)
    745         {
    746             std::vector<Utility::Architecture> supportedArchitectures;
    747 
    748             for (auto const& sfsArchitecture : appFile.GetApplicabilityDetails().GetArchitectures())
    749             {
    750                 auto convertedArchitecture = ConvertFromSfsArchitecture(sfsArchitecture);
    751                 if (convertedArchitecture == Utility::Architecture::Unknown)
    752                 {
    753                     continue;
    754                 }
    755 
    756                 if (requiredArchitecture == Utility::Architecture::Unknown || // No required architecture
    757                     convertedArchitecture == requiredArchitecture)
    758                 {
    759                     supportedArchitectures.emplace_back(convertedArchitecture);
    760                 }
    761             }
    762 
    763             return supportedArchitectures;
    764         }
    765 
    766         std::string GetSfsPackageFileExtension(const SFS::AppFile& appFile)
    767         {
    768             return std::filesystem::path{ appFile.GetFileId() }.extension().u8string();
    769         }
    770 
    771         bool IsFileExtensionSupported(std::string_view fileExtension)
    772         {
    773             for (auto const& supportedFileType : SupportedFileTypes)
    774             {
    775                 if (Utility::CaseInsensitiveEquals(supportedFileType, fileExtension))
    776                 {
    777                     return true;
    778                 }
    779             }
    780 
    781             return false;
    782         }
    783 
    784         // The file name will be {Name}_{Version}_{Platform list}_{Arch list}.{File Extension}
    785         // If the file name is longer than 256, file moniker will be used.
    786         std::string GetSfsPackageFileNameForDownload(
    787             const std::string& packageName,
    788             const Utility::UInt64Version& packageVersion,
    789             const std::vector<Manifest::PlatformEnum>& supportedPlatforms,
    790             const std::vector<Utility::Architecture>& supportedArchitectures,
    791             const std::string& fileExtension,
    792             const std::string& fileMoniker)
    793         {
    794             std::string platformString;
    795             for (auto platform : supportedPlatforms)
    796             {
    797                 platformString += std::string{ Manifest::PlatformToString(platform, true) } + '.';
    798             }
    799             platformString.resize(platformString.size() - 1);
    800 
    801             std::string architectureString;
    802             for (auto architecture : supportedArchitectures)
    803             {
    804                 architectureString += std::string{ Utility::ToString(architecture) } + '.';
    805             }
    806             architectureString.resize(architectureString.size() - 1);
    807 
    808             std::string fileName =
    809                 packageName + '_' +
    810                 packageVersion.ToString() + '_' +
    811                 platformString + '_' +
    812                 architectureString +
    813                 fileExtension;
    814 
    815             if (fileName.length() < 256)
    816             {
    817                 return fileName;
    818             }
    819             else
    820             {
    821                 return fileMoniker + fileExtension;
    822             }
    823         }
    824 
    825         void SfsClientLoggingCallback(const SFS::LogData& logData)
    826         {
    827             std::string message = "Message: " + std::string{ logData.message };
    828             message += " File: " + std::string{ logData.file };
    829             message += " Line: " + std::to_string(logData.line);
    830             message += " Function: " + std::string{ logData.function };
    831 
    832             switch (logData.severity)
    833             {
    834             case SFS::LogSeverity::Verbose:
    835                 AICLI_LOG(Core, Verbose, << message);
    836                 break;
    837             case SFS::LogSeverity::Info:
    838                 AICLI_LOG(Core, Info, << message);
    839                 break;
    840             case SFS::LogSeverity::Warning:
    841                 AICLI_LOG(Core, Warning, << message);
    842                 break;
    843             case SFS::LogSeverity::Error:
    844                 AICLI_LOG(Core, Error, << message);
    845                 break;
    846             }
    847         }
    848 
    849         const std::unique_ptr<SFS::SFSClient>& GetSfsClientInstance()
    850         {
    851             static std::unique_ptr<SFS::SFSClient> s_sfsClient;
    852             static std::once_flag s_sfsClientInitializeOnce;
    853 
    854             std::call_once(s_sfsClientInitializeOnce,
    855                 [&]()
    856                 {
    857                     SFS::ClientConfig config;
    858                     config.accountId = "storeapps";
    859                     config.instanceId = "storeapps";
    860                     config.logCallbackFn = SfsClientLoggingCallback;
    861 
    862                     auto result = SFS::SFSClient::Make(config, s_sfsClient);
    863                     if (!result)
    864                     {
    865                         AICLI_LOG(Core, Error, << "Failed to initialize SfsClient. Error code: " << result.GetCode() << " Message: " << result.GetMsg());
    866                         THROW_HR_MSG(APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED, "Failed to initialize SfsClient. ErrorCode: %lu Message: %hs", result.GetCode(), result.GetMsg().c_str());
    867                     }
    868                 });
    869 
    870             return s_sfsClient;
    871         }
    872 
    873         std::vector<MSStoreDownloadFile> PopulateSfsAppFileToMSStoreDownloadFileVector(
    874             const std::vector<SFS::AppFile>& appFiles,
    875             Utility::Architecture requiredArchitecture = Utility::Architecture::Unknown,
    876             Manifest::PlatformEnum requiredPlatform = Manifest::PlatformEnum::Unknown,
    877             const std::optional<Utility::UInt64Version>& targetOSVersion = std::nullopt)
    878         {
    879             using PlatformAndArchitectureKey = std::pair<Manifest::PlatformEnum, Utility::Architecture>;
    880 
    881             // Since the server may return multiple versions of the same package, we'll use this map to record the one with latest version
    882             // for each Platform|Architecture pair.
    883             std::map<PlatformAndArchitectureKey, MSStoreDownloadFile> downloadFilesMap;
    884 
    885             for (auto const& appFile : appFiles)
    886             {
    887                 AICLI_LOG(Core, Info, << "Examining package [" << appFile.GetFileMoniker() << " (" << appFile.GetFileId() << ")] for download...");
    888 
    889                 // Filter out unsupported packages
    890                 auto supportedPlatforms = GetSfsPackageFileSupportedPlatforms(appFile, requiredPlatform, targetOSVersion);
    891                 if (supportedPlatforms.empty())
    892                 {
    893                     AICLI_LOG(Core, Verbose, << "  package has no applicable platform.");
    894                     continue;
    895                 }
    896                 auto supportedArchitectures = GetSfsPackageFileSupportedArchitectures(appFile, requiredArchitecture);
    897                 if (supportedArchitectures.empty())
    898                 {
    899                     AICLI_LOG(Core, Verbose, << "  package has no applicable architecture.");
    900                     continue;
    901                 }
    902                 std::string fileExtension = GetSfsPackageFileExtension(appFile);
    903                 if (!IsFileExtensionSupported(fileExtension))
    904                 {
    905                     AICLI_LOG(Core, Verbose, << "  package has unsupported file type [" << fileExtension << "].");
    906                     continue;
    907                 }
    908 
    909                 MSStoreDownloadFile downloadFile;
    910                 downloadFile.Url = appFile.GetUrl();
    911                 // The sha256 hash was base64 encoded
    912                 downloadFile.Sha256 = JSON::Base64Decode(appFile.GetHashes().at(SFS::HashType::Sha256));
    913                 auto packageInfo = Msix::GetPackageIdInfoFromFullName(appFile.GetFileMoniker());
    914                 downloadFile.Version = packageInfo.Version;
    915                 downloadFile.FileName = GetSfsPackageFileNameForDownload(
    916                     packageInfo.Name, packageInfo.Version, supportedPlatforms,
    917                     supportedArchitectures, fileExtension, appFile.GetFileMoniker());
    918 
    919                 // Update the platform architecture map with latest package if applicable
    920                 for (auto supportedPlatform : supportedPlatforms)
    921                 {
    922                     for (auto supportedArchitecture : supportedArchitectures)
    923                     {
    924                         PlatformAndArchitectureKey downloadFileKey{ supportedPlatform, supportedArchitecture };
    925                         if (downloadFile.Version > downloadFilesMap[downloadFileKey].Version)
    926                         {
    927                             downloadFilesMap[downloadFileKey] = downloadFile;
    928                         }
    929                     }
    930                 }
    931             }
    932 
    933             // Generate MSStoreDownloadFile vector and remove duplication.
    934             std::vector<MSStoreDownloadFile> result;
    935             for (auto& downloadFileEntry : downloadFilesMap)
    936             {
    937                 if (std::find_if(result.begin(), result.end(),
    938                     [&](const MSStoreDownloadFile& downloadFile)
    939                     {
    940                         return Utility::CaseInsensitiveEquals(downloadFile.FileName, downloadFileEntry.second.FileName);
    941                     }) == result.end())
    942                 {
    943                     result.emplace_back(std::move(downloadFileEntry.second));
    944                 }
    945             }
    946 
    947             return result;
    948         }
    949 
    950         MSStoreDownloadInfo CallSfsClientAndGetMSStoreDownloadInfo(
    951             std::string_view wuCategoryId,
    952             Utility::Architecture requiredArchitecture,
    953             Manifest::PlatformEnum requiredPlatform,
    954             const std::optional<Utility::UInt64Version>& targetOSVersion)
    955         {
    956             AICLI_LOG(Core, Info, << "CallSfsClientAndGetMSStoreDownloadInfo with WuCategoryId: " << wuCategoryId
    957                 << " Architecture: " << Utility::ToString(requiredArchitecture) << " Platform: " << Manifest::PlatformToString(requiredPlatform)
    958                 << " Target OS Version: " << (targetOSVersion ? targetOSVersion.value().ToString() : "any"));
    959 
    960             std::vector<SFS::AppContent> appContents;
    961 
    962 #ifndef AICLI_DISABLE_TEST_HOOKS
    963             if (TestHooks::s_SfsClient_AppContents_Override)
    964             {
    965                 appContents = (*TestHooks::s_SfsClient_AppContents_Override)(wuCategoryId);
    966             }
    967             else
    968 #endif
    969             {
    970                 SFS::RequestParams sfsClientRequest;
    971                 sfsClientRequest.productRequests = { {std::string{ wuCategoryId }, {}} };
    972                 const auto& proxyUri = AppInstaller::Settings::Network().GetProxyUri();
    973                 if (proxyUri)
    974                 {
    975                     AICLI_LOG(Core, Info, << "Passing proxy to SFS client " << *proxyUri);
    976                     sfsClientRequest.proxy = *proxyUri;
    977                 }
    978 
    979                 auto requestResult = GetSfsClientInstance()->GetLatestAppDownloadInfo(sfsClientRequest, appContents);
    980                 if (!requestResult)
    981                 {
    982                     if (requestResult.GetCode() == SFS::Result::Code::HttpNotFound)
    983                     {
    984                         AICLI_LOG(Core, Error, << "Failed to call SfsClient GetLatestAppDownloadInfo. Package not found.");
    985                         THROW_HR_MSG(APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED, "Failed to call SfsClient GetLatestAppDownloadInfo. Package download not supported.");
    986                     }
    987                     else
    988                     {
    989                         AICLI_LOG(Core, Error, << "Failed to call SfsClient GetLatestAppDownloadInfo. Error code: " << requestResult.GetCode() << " Message: " << requestResult.GetMsg());
    990                         THROW_HR_MSG(APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED, "Failed to call SfsClient GetLatestAppDownloadInfo. ErrorCode: %lu Message: %hs", requestResult.GetCode(), requestResult.GetMsg().c_str());
    991                     }
    992                 }
    993             }
    994 
    995             THROW_HR_IF(E_UNEXPECTED, appContents.empty());
    996 
    997             MSStoreDownloadInfo result;
    998             // Currently for app downloads, the result vector is always size 1.
    999             const auto& appContent = appContents.at(0);
   1000 
   1001             // Populate main packages
   1002             result.MainPackages = PopulateSfsAppFileToMSStoreDownloadFileVector(appContent.GetFiles(), requiredArchitecture, requiredPlatform, targetOSVersion);
   1003 
   1004             // Populate dependency packages
   1005             for (auto const& dependencyEntry : appContent.GetPrerequisites())
   1006             {
   1007                 // Not passing in required platform for dependencies. Dependencies are mostly Windows.Universal.
   1008                 auto dependencyPackages = PopulateSfsAppFileToMSStoreDownloadFileVector(dependencyEntry.GetFiles(), requiredArchitecture, Manifest::PlatformEnum::Unknown, targetOSVersion);
   1009                 std::move(dependencyPackages.begin(), dependencyPackages.end(), std::inserter(result.DependencyPackages, result.DependencyPackages.end()));
   1010             }
   1011 
   1012             if (result.MainPackages.empty())
   1013             {
   1014                 AICLI_LOG(Core, Error, << "No applicable SFS main package.");
   1015                 THROW_HR(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE);
   1016             }
   1017 
   1018             return result;
   1019         }
   1020     }
   1021 #endif
   1022 
   1023     namespace LicensingDetails
   1024     {
   1025         // Json response fields
   1026         constexpr std::string_view License = "license"sv;
   1027         constexpr std::string_view Keys = "keys"sv;
   1028         constexpr std::string_view Value = "value"sv;
   1029 
   1030         // Licensing rest endpoint
   1031         constexpr std::string_view LicensingRestEndpoint = "https://licensing.md.mp.microsoft.com/v9.0/licenses/offlineContent";
   1032         constexpr std::string_view ContentId = "contentId"sv;
   1033         constexpr std::string_view From = "From"sv;
   1034 
   1035         // Response:
   1036         // {
   1037         //   "license": {
   1038         //     "keys": [ // returned as array for future, for now only 1 key
   1039         //       {
   1040         //         "value": "base64 encoded string"
   1041         //       }
   1042         //     ]
   1043         //   }
   1044         // }
   1045         std::vector<BYTE> GetLicensing(std::string_view contentId, const Http::HttpClientHelper::HttpRequestHeaders& authHeaders)
   1046         {
   1047             AICLI_LOG(Core, Error, << "GetLicensing with ContentId: " << contentId);
   1048 
   1049             AppInstaller::Http::HttpClientHelper httpClientHelper;
   1050 
   1051 #ifndef AICLI_DISABLE_TEST_HOOKS
   1052             if (TestHooks::s_Licensing_HttpPipelineStage_Override)
   1053             {
   1054                 httpClientHelper = AppInstaller::Http::HttpClientHelper{ TestHooks::s_Licensing_HttpPipelineStage_Override };
   1055             }
   1056 #endif
   1057 
   1058             web::json::value requestBody;
   1059             requestBody[JSON::GetUtilityString(ContentId)] = web::json::value::string(JSON::GetUtilityString(contentId));
   1060             Http::HttpClientHelper::HttpRequestHeaders requestHeaders;
   1061             requestHeaders.insert_or_assign(JSON::GetUtilityString(From), L"winget-cli");
   1062 
   1063             std::optional<web::json::value> licensingResponseObject = std::nullopt;
   1064             try
   1065             {
   1066                 licensingResponseObject = httpClientHelper.HandlePost(
   1067                     JSON::GetUtilityString(LicensingRestEndpoint), requestBody, requestHeaders, authHeaders);
   1068             }
   1069             catch (const wil::ResultException& re)
   1070             {
   1071                 if (re.GetErrorCode() == HTTP_E_STATUS_FORBIDDEN)
   1072                 {
   1073                     AICLI_LOG(CLI, Error, << "Getting MSStore package license failed. The Microsoft Entra Id account does not have privilege.");
   1074                     THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN);
   1075                 }
   1076                 else
   1077                 {
   1078                     AICLI_LOG(CLI, Error, << "Getting MSStore package license failed. Error code: " << re.GetErrorCode());
   1079                     THROW_HR(re.GetErrorCode());
   1080                 }
   1081             }
   1082 
   1083             if (!licensingResponseObject || licensingResponseObject->is_null())
   1084             {
   1085                 AICLI_LOG(Core, Error, << "Empty licensing response");
   1086                 THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED);
   1087             }
   1088 
   1089             std::optional<std::reference_wrapper<const web::json::value>> license = JSON::GetJsonValueFromNode(licensingResponseObject.value(), JSON::GetUtilityString(License));
   1090             if (!license)
   1091             {
   1092                 AICLI_LOG(Core, Error, << "Missing license node");
   1093                 THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED);
   1094             }
   1095 
   1096             auto keys = JSON::GetRawJsonArrayFromJsonNode(license.value().get(), JSON::GetUtilityString(Keys));
   1097             if (!keys || keys->get().size() == 0)
   1098             {
   1099                 AICLI_LOG(Core, Error, << "Missing keys or empty keys");
   1100                 THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED);
   1101             }
   1102 
   1103             std::string base64LicenseContent = JSON::GetRawStringValueFromJsonNode(keys->get().at(0), JSON::GetUtilityString(Value)).value_or("");
   1104             if (base64LicenseContent.empty())
   1105             {
   1106                 AICLI_LOG(Core, Error, << "Missing license content");
   1107                 THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED);
   1108             }
   1109 
   1110             return JSON::Base64Decode(base64LicenseContent);
   1111         }
   1112     }
   1113 
   1114     namespace
   1115     {
   1116         Http::HttpClientHelper::HttpRequestHeaders GetAuthHeaders(std::unique_ptr<Authentication::Authenticator>& authenticator)
   1117         {
   1118             if (!authenticator)
   1119             {
   1120                 return {};
   1121             }
   1122 
   1123             Http::HttpClientHelper::HttpRequestHeaders result;
   1124 
   1125             auto authResult = authenticator->AuthenticateForToken();
   1126             if (FAILED(authResult.Status))
   1127             {
   1128                 AICLI_LOG(Repo, Error, << "Authentication failed. Result: " << authResult.Status);
   1129                 THROW_HR_MSG(authResult.Status, "Failed to authenticate for MicrosoftEntraId");
   1130             }
   1131             result.insert_or_assign(web::http::header_names::authorization, JSON::GetUtilityString(Authentication::CreateBearerToken(authResult.Token)));
   1132 
   1133             return result;
   1134         }
   1135     }
   1136 
   1137     MSStoreDownloadContext::MSStoreDownloadContext(
   1138         std::string productId,
   1139         AppInstaller::Utility::Architecture architecture,
   1140         Manifest::PlatformEnum platform,
   1141         std::string locale,
   1142         AppInstaller::Authentication::AuthenticationArguments authArgs) :
   1143         m_productId(std::move(productId)), m_architecture(architecture), m_platform(platform), m_locale(std::move(locale))
   1144     {
   1145 #ifndef AICLI_DISABLE_TEST_HOOKS
   1146         if (!TestHooks::s_DisplayCatalog_HttpPipelineStage_Override)
   1147 #endif
   1148         {
   1149             Authentication::MicrosoftEntraIdAuthenticationInfo displayCatalogMicrosoftEntraIdAuthInfo;
   1150             displayCatalogMicrosoftEntraIdAuthInfo.Resource = "https://bigcatalog.commerce.microsoft.com";
   1151             Authentication::AuthenticationInfo displayCatalogAuthInfo;
   1152             displayCatalogAuthInfo.Type = Authentication::AuthenticationType::MicrosoftEntraId;
   1153             displayCatalogAuthInfo.MicrosoftEntraIdInfo = std::move(displayCatalogMicrosoftEntraIdAuthInfo);
   1154 
   1155             m_displayCatalogAuthenticator = std::make_unique<Authentication::Authenticator>(std::move(displayCatalogAuthInfo), authArgs);
   1156         }
   1157 
   1158 #ifndef AICLI_DISABLE_TEST_HOOKS
   1159         if (!TestHooks::s_Licensing_HttpPipelineStage_Override)
   1160 #endif
   1161         {
   1162             Authentication::MicrosoftEntraIdAuthenticationInfo licensingMicrosoftEntraIdAuthInfo;
   1163             licensingMicrosoftEntraIdAuthInfo.Resource = "c5e1cb0d-5d24-4b1a-b291-ec684152b2ba";
   1164             Authentication::AuthenticationInfo licensingAuthInfo;
   1165             licensingAuthInfo.Type = Authentication::AuthenticationType::MicrosoftEntraId;
   1166             licensingAuthInfo.MicrosoftEntraIdInfo = std::move(licensingMicrosoftEntraIdAuthInfo);
   1167 
   1168             m_licensingAuthenticator = std::make_unique<Authentication::Authenticator>(std::move(licensingAuthInfo), authArgs);
   1169         }
   1170     }
   1171 
   1172     void MSStoreDownloadContext::TargetOSVersion(std::optional<Utility::UInt64Version> targetOSVersion)
   1173     {
   1174         m_targetOSVersion = std::move(targetOSVersion);
   1175     }
   1176 
   1177     MSStoreDownloadInfo MSStoreDownloadContext::GetDownloadInfo()
   1178     {
   1179 #ifndef WINGET_DISABLE_FOR_FUZZING
   1180         auto displayCatalogPackage = DisplayCatalogDetails::CallDisplayCatalogAndGetPreferredPackage(m_productId, m_locale, m_architecture, GetAuthHeaders(m_displayCatalogAuthenticator));
   1181         auto downloadInfo = SfsClientDetails::CallSfsClientAndGetMSStoreDownloadInfo(displayCatalogPackage.WuCategoryId, m_architecture, m_platform, m_targetOSVersion);
   1182         downloadInfo.ContentId = displayCatalogPackage.ContentId;
   1183         return downloadInfo;
   1184 #else
   1185         return {};
   1186 #endif
   1187     }
   1188 
   1189     std::vector<BYTE> MSStoreDownloadContext::GetLicense(std::string_view contentId)
   1190     {
   1191         return LicensingDetails::GetLicensing(contentId, GetAuthHeaders(m_licensingAuthenticator));
   1192     }
   1193 }