blob: 978fb0ff7f8c6ecc74f7157df866fae5bbf4ff3c [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/web_package/signed_web_bundles/attribute_map_parser.h"
#include <variant>
#include "base/containers/contains.h"
#include "base/containers/extend.h"
#include "base/strings/stringprintf.h"
#include "base/types/expected_macros.h"
#include "components/web_package/input_reader.h"
#include "third_party/abseil-cpp/absl/functional/overload.h"
namespace web_package {
AttributeMapParser::AttributeMapParser(mojom::BundleDataSource& data_source,
AttributeMapParsedCallback callback)
: data_source_(data_source), callback_(std::move(callback)) {}
AttributeMapParser::~AttributeMapParser() = default;
void AttributeMapParser::Parse(uint64_t offset_in_stream) {
offset_in_stream_ = offset_in_stream;
data_source_->Read(
offset_in_stream_, kMaxCBORItemHeaderSize,
base::BindOnce(&AttributeMapParser::ReadAttributesMapHeader,
weak_factory_.GetWeakPtr()));
}
void AttributeMapParser::ReadAttributesMapHeader(
const std::optional<BinaryData>& data) {
if (!data) {
RunErrorCallback(
"Error reading signature stack entry's attributes header.");
return;
}
std::optional<uint64_t> attributes_map_length = ReadCborData(
*data,
[](InputReader* input) { return input->ReadCBORHeader(CBORType::kMap); });
if (!attributes_map_length) {
RunErrorCallback(
"Cannot parse the size of signature stack entry's attributes.");
return;
};
attributes_entries_left_ = *attributes_map_length;
ReadNextAttributeEntry();
}
void AttributeMapParser::ReadNextAttributeEntry() {
CHECK_GE(attributes_entries_left_, 0ul);
if (attributes_entries_left_ > 0) {
--attributes_entries_left_;
data_source_->Read(
offset_in_stream_, kMaxCBORItemHeaderSize,
base::BindOnce(&AttributeMapParser::ReadAttributeNameCborHeader,
weak_factory_.GetWeakPtr()));
} else {
RunSuccessCallback();
}
}
void AttributeMapParser::ReadAttributeNameCborHeader(
const std::optional<BinaryData>& data) {
if (!data) {
RunErrorCallback("Error reading the header for attribute name.");
return;
}
std::optional<uint64_t> attribute_name_size =
ReadCborData(*data, [](InputReader* input) {
return input->ReadCBORHeader(CBORType::kTextString);
});
if (!attribute_name_size) {
RunErrorCallback("The value of the attribute name must be a text string.");
return;
}
data_source_->Read(
offset_in_stream_, *attribute_name_size,
base::BindOnce(&AttributeMapParser::ReadAttributeName,
weak_factory_.GetWeakPtr(), *attribute_name_size));
}
void AttributeMapParser::ReadAttributeName(
uint64_t attribute_name_length,
const std::optional<BinaryData>& data) {
if (!data) {
RunErrorCallback("Error reading attribute key.");
return;
}
std::optional<std::string_view> attribute_name =
ReadCborData(*data, [attribute_name_length](InputReader* input) {
return input->ReadString(attribute_name_length);
});
if (!attribute_name) {
RunErrorCallback("Error reading attribute key.");
return;
}
if (base::Contains(attributes_map_, *attribute_name)) {
RunErrorCallback(base::StringPrintf(
"Found duplicate attribute name <%s> in the attributes map.",
std::string(*attribute_name).c_str()));
return;
}
data_source_->Read(
offset_in_stream_, kMaxCBORItemHeaderSize,
base::BindOnce(&AttributeMapParser::ReadAttributeValueCborHeader,
weak_factory_.GetWeakPtr(), std::string{*attribute_name}));
}
void AttributeMapParser::ReadAttributeValueCborHeader(
std::string attribute_name,
const std::optional<BinaryData>& data) {
if (!data) {
RunErrorCallback(
base::StringPrintf("Error reading attribute value header for <%s>.",
attribute_name.c_str()));
return;
}
std::optional<CBORHeader> result = ReadCborData(
*data, [](InputReader* input) { return input->ReadCBORHeader(); });
if (!result) {
RunErrorCallback(
base::StringPrintf("Error reading attribute value header for <%s>.",
attribute_name.c_str()));
return;
}
RETURN_IF_ERROR(
std::visit(
absl::Overload{
[&](bool value) -> base::expected<void, std::string> {
attributes_map_.emplace(std::move(attribute_name), value);
ReadNextAttributeEntry();
return base::ok();
},
[&](int64_t value) -> base::expected<void, std::string> {
attributes_map_.emplace(std::move(attribute_name), value);
ReadNextAttributeEntry();
return base::ok();
},
[&](const CBORHeader::StringInfo& info)
-> base::expected<void, std::string> {
data_source_->Read(
offset_in_stream_, info.byte_length,
base::BindOnce(
&AttributeMapParser::ReadStringAttributeValue,
weak_factory_.GetWeakPtr(), std::move(attribute_name),
info.type));
return base::ok();
},
[&](const CBORHeader::ContainerInfo&)
-> base::expected<void, std::string> {
return base::unexpected(base::StringPrintf(
"Attribute value for <%s> is a map/array; nested "
"attributes are currently not supported.",
attribute_name.c_str()));
}},
result->data),
[&](std::string error) { RunErrorCallback(std::move(error)); });
}
void AttributeMapParser::ReadStringAttributeValue(
std::string attribute_name,
StringType string_type,
const std::optional<BinaryData>& data) {
if (!data) {
RunErrorCallback(base::StringPrintf(
"Error reading attribute value for <%s>.", attribute_name.c_str()));
return;
}
switch (string_type) {
case StringType::kByteString: {
attributes_map_.emplace(std::move(attribute_name), *data);
offset_in_stream_ += data->size();
} break;
case StringType::kTextString: {
std::optional<std::string_view> value = ReadCborData(
*data,
[&](InputReader* input) { return input->ReadString(data->size()); });
if (!value) {
RunErrorCallback(base::StringPrintf(
"Error reading attribute value for <%s>.", attribute_name.c_str()));
return;
}
attributes_map_.emplace(std::move(attribute_name), *value);
}
}
ReadNextAttributeEntry();
}
} // namespace web_package