PackageVersionDataManifest.cpp (7558B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "Public/winget/PackageVersionDataManifest.h" 5 #include "Public/winget/Yaml.h" 6 #include <AppInstallerErrors.h> 7 #include <AppInstallerLogging.h> 8 #include <AppInstallerStrings.h> 9 10 using namespace std::string_view_literals; 11 12 namespace AppInstaller::Manifest 13 { 14 // These shortened names save some bytes since humans are neither authoring them nor reading them (except to debug). 15 static constexpr std::string_view s_FieldName_SchemaVersion = "sV"sv; 16 static constexpr std::string_view s_FieldName_VersionData = "vD"sv; 17 static constexpr std::string_view s_FieldName_Version = "v"sv; 18 static constexpr std::string_view s_FieldName_ArpMinVersion = "aMiV"sv; 19 static constexpr std::string_view s_FieldName_ArpMaxVersion = "aMaV"sv; 20 static constexpr std::string_view s_FieldName_RelativePath = "rP"sv; 21 static constexpr std::string_view s_FieldName_Sha256Hash = "s256H"sv; 22 23 static constexpr std::string_view s_SchemaVersion_1_0 = "1.0"sv; 24 25 static constexpr DWORD CompressionAlgorithm = COMPRESS_ALGORITHM_MSZIP; 26 static constexpr bool CompressionSetLevel1 = false; 27 28 namespace anon 29 { 30 std::string GetRequiredChildString(const YAML::Node& node, std::string_view childName) 31 { 32 const YAML::Node& childNode = node.GetChildNode(childName); 33 THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !childNode.IsScalar()); 34 return childNode.as<std::string>(); 35 } 36 37 std::optional<std::string> GetOptionalChildString(const YAML::Node& node, std::string_view childName) 38 { 39 const YAML::Node& childNode = node.GetChildNode(childName); 40 return childNode.IsScalar() ? std::make_optional(childNode.as<std::string>()) : std::nullopt; 41 } 42 43 void Deserialize_1_0(const YAML::Node& document, PackageVersionDataManifest& manifest) 44 { 45 const YAML::Node& versionDataItems = document.GetChildNode(s_FieldName_VersionData); 46 THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !versionDataItems.IsSequence()); 47 48 for (const YAML::Node& item : versionDataItems.Sequence()) 49 { 50 THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !item.IsMap()); 51 52 PackageVersionDataManifest::VersionData versionData; 53 54 versionData.Version.Assign(GetRequiredChildString(item, s_FieldName_Version)); 55 versionData.ArpMinVersion = GetOptionalChildString(item, s_FieldName_ArpMinVersion); 56 versionData.ArpMaxVersion = GetOptionalChildString(item, s_FieldName_ArpMaxVersion); 57 versionData.ManifestRelativePath = GetRequiredChildString(item, s_FieldName_RelativePath); 58 versionData.ManifestHash = GetRequiredChildString(item, s_FieldName_Sha256Hash); 59 60 manifest.AddVersion(std::move(versionData)); 61 } 62 } 63 } 64 65 std::string_view PackageVersionDataManifest::VersionManifestFileName() 66 { 67 return "versionData.yml"sv; 68 } 69 70 std::string_view PackageVersionDataManifest::VersionManifestCompressedFileName() 71 { 72 return "versionData.mszyml"sv; 73 } 74 75 std::filesystem::path PackageVersionDataManifest::GetRelativeDirectoryPath(std::string_view packageIdentifier, std::string_view manifestHash) 76 { 77 std::filesystem::path result = "packages"; 78 result /= Utility::ConvertToUTF16(packageIdentifier); 79 result /= manifestHash.substr(0, 8); 80 return result; 81 } 82 83 Compression::Compressor PackageVersionDataManifest::CreateCompressor() 84 { 85 Compression::Compressor result(CompressionAlgorithm); 86 if constexpr (CompressionSetLevel1) 87 { 88 result.SetInformation(COMPRESS_INFORMATION_CLASS_LEVEL, 1); 89 } 90 return result; 91 } 92 93 Compression::Decompressor PackageVersionDataManifest::CreateDecompressor() 94 { 95 return Compression::Decompressor(CompressionAlgorithm); 96 } 97 98 PackageVersionDataManifest::VersionData::VersionData( 99 const Utility::VersionAndChannel& versionAndChannel, 100 std::optional<std::string> arpMinVersion, 101 std::optional<std::string> arpMaxVersion, 102 std::optional<std::string> relativePath, 103 std::optional<std::string> manifestHash) : 104 Version(versionAndChannel.GetVersion()), 105 ArpMinVersion(std::move(arpMinVersion)), 106 ArpMaxVersion(std::move(arpMaxVersion)), 107 ManifestRelativePath(std::move(relativePath).value_or("")), 108 ManifestHash(std::move(manifestHash).value_or("")) 109 { 110 if (ArpMinVersion && ArpMinVersion->empty()) 111 { 112 ArpMinVersion.reset(); 113 } 114 115 if (ArpMaxVersion && ArpMaxVersion->empty()) 116 { 117 ArpMaxVersion.reset(); 118 } 119 } 120 121 void PackageVersionDataManifest::AddVersion(VersionData&& versionData) 122 { 123 m_versions.emplace_back(std::move(versionData)); 124 } 125 126 const std::vector<PackageVersionDataManifest::VersionData>& PackageVersionDataManifest::Versions() const 127 { 128 return m_versions; 129 } 130 131 std::string PackageVersionDataManifest::Serialize() 132 { 133 YAML::Emitter out; 134 out << YAML::BeginMap; 135 out << YAML::Key << s_FieldName_SchemaVersion << YAML::Value << s_SchemaVersion_1_0; 136 137 out << YAML::Key << s_FieldName_VersionData; 138 out << YAML::BeginSeq; 139 140 for (const auto& version : m_versions) 141 { 142 out << YAML::BeginMap; 143 out << YAML::Key << s_FieldName_Version << YAML::Value << version.Version.ToString(); 144 if (version.ArpMinVersion) 145 { 146 out << YAML::Key << s_FieldName_ArpMinVersion << YAML::Value << version.ArpMinVersion.value(); 147 } 148 if (version.ArpMaxVersion) 149 { 150 out << YAML::Key << s_FieldName_ArpMaxVersion << YAML::Value << version.ArpMaxVersion.value(); 151 } 152 out << YAML::Key << s_FieldName_RelativePath << YAML::Value << version.ManifestRelativePath; 153 out << YAML::Key << s_FieldName_Sha256Hash << YAML::Value << version.ManifestHash; 154 out << YAML::EndMap; 155 } 156 157 out << YAML::EndSeq; 158 out << YAML::EndMap; 159 160 return out.str(); 161 } 162 163 void PackageVersionDataManifest::Deserialize(std::string_view input) 164 { 165 AICLI_LOG_LARGE_STRING(Core, Verbose, << "PackageVersionDataManifest deserializing:", input); 166 167 YAML::Node document = YAML::Load(input); 168 THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !document.IsMap()); 169 170 const YAML::Node& schemaVersionNode = document.GetChildNode(s_FieldName_SchemaVersion); 171 THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !schemaVersionNode.IsScalar()); 172 173 Utility::Version schemaVersion{ schemaVersionNode.as<std::string>() }; 174 175 if (schemaVersion.PartAt(0).Integer == 1) 176 { 177 anon::Deserialize_1_0(document, *this); 178 } 179 else 180 { 181 THROW_HR(APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION); 182 } 183 } 184 185 void PackageVersionDataManifest::Deserialize(const std::vector<uint8_t>& input) 186 { 187 Deserialize(std::string_view{ reinterpret_cast<const char*>(input.data()), input.size() }); 188 } 189 }