blob: ce98f4236aa9c6606c10ac4e73212cc38b9c1a87 [file]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "jumbf/parse.h"
#include <algorithm>
#include <optional>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/status_macros.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "jumbf/box.h"
#include "jumbf/constants.h"
#include "jumbf/internal/consume_box.h"
#include "jumbf/internal/intermediate.h"
#include "jumbf/internal/parse_embedded_file_description_box.h"
#include "jumbf/internal/parse_super_box.h"
namespace jumbf {
using ::jumbf_internal::ConsumeBox;
using ::jumbf_internal::IntermediateBox;
using ::jumbf_internal::IntermediateDescriptionBox;
using ::jumbf_internal::ParseEmbeddedFileDescriptionBox;
namespace {
absl::StatusOr<ContentBox> ParseContentBox(const IntermediateBox& unwrapped_box,
int recursion_limit);
absl::StatusOr<SuperBox> ParseSuperBox(
const IntermediateBox& unwrapped_super_box, int recursion_limit);
absl::StatusOr<DescriptionBox> FinalizeDescriptionBox(
const IntermediateDescriptionBox& intermediate_box, int recursion_limit) {
std::vector<ContentBox> private_contents;
if (intermediate_box.private_box.has_value()) {
const IntermediateBox& private_box = *intermediate_box.private_box;
absl::string_view private_content_view = private_box.payload;
if (private_box.type == kPrivateContentBoxType) {
// Special private box type, repeated list of arbitrary boxes.
while (!private_content_view.empty()) {
ABSL_ASSIGN_OR_RETURN(
auto unwrapped_private_element,
ConsumeBox(&private_content_view, /*allow_implicit_length=*/false),
_.SetPrepend() << "could not unwrap private content box for index "
<< private_contents.size() << ": ");
ABSL_ASSIGN_OR_RETURN(
auto private_element,
ParseContentBox(unwrapped_private_element, recursion_limit),
_.SetPrepend() << "could not parse private content box for index "
<< private_contents.size() << ": ");
private_contents.push_back(std::move(private_element));
}
} else {
ABSL_ASSIGN_OR_RETURN(auto private_element,
ParseContentBox(private_box, recursion_limit));
private_contents.push_back(std::move(private_element));
}
}
return DescriptionBox{
.type_uuid = intermediate_box.type_uuid,
.requestable = intermediate_box.requestable,
.label = intermediate_box.label,
.id = intermediate_box.id,
.unvalidated_hash = intermediate_box.hash,
.private_content = std::move(private_contents),
};
}
absl::StatusOr<ContentBox> ParseContentBox(const IntermediateBox& unwrapped_box,
int recursion_limit) {
if (recursion_limit < 0) {
// All negative values mean "no recursion limit". Pick -1 to avoid worries
// about underflow when we subtract from it.
recursion_limit = -1;
}
switch (unwrapped_box.type) {
case kCborBoxType:
return ContentBox{
.raw_bytes = unwrapped_box.serialized,
.payload =
CborBox{
.payload = unwrapped_box.payload,
},
};
case kSuperBoxType: {
if (recursion_limit == 0) {
return ContentBox{
.raw_bytes = unwrapped_box.serialized,
.payload =
SerializedSuperbox{
.serialized = unwrapped_box.serialized,
},
};
} else {
ABSL_ASSIGN_OR_RETURN(
auto super_box,
ParseSuperBox(unwrapped_box, std::max(recursion_limit - 1, -1)));
return ContentBox{
.raw_bytes = unwrapped_box.serialized,
.payload = std::move(super_box),
};
}
break;
}
case kEmbeddedFileDescriptionBoxType: {
ABSL_ASSIGN_OR_RETURN(auto efdb,
ParseEmbeddedFileDescriptionBox(unwrapped_box));
return ContentBox{
.raw_bytes = unwrapped_box.serialized,
.payload = std::move(efdb),
};
}
case kBinaryDataBoxType:
return ContentBox{
.raw_bytes = unwrapped_box.serialized,
.payload = BinaryDataBox{.payload = unwrapped_box.payload},
};
case kC2paSaltBoxType:
return ContentBox{
.raw_bytes = unwrapped_box.serialized,
.payload = C2paSaltBox{.salt = unwrapped_box.payload},
};
case kJsonBoxType:
return ContentBox{
.raw_bytes = unwrapped_box.serialized,
.payload = JsonBox{.payload = unwrapped_box.payload},
};
case kUuidBoxType:
return ContentBox{
.raw_bytes = unwrapped_box.serialized,
.payload = UuidBox{.payload = unwrapped_box.payload},
};
default:
return ContentBox{
.raw_bytes = unwrapped_box.serialized,
.payload =
UnknownBox{
.type = unwrapped_box.type,
.payload = unwrapped_box.payload,
},
};
}
}
absl::StatusOr<SuperBox> ParseSuperBox(
const IntermediateBox& unwrapped_super_box, int recursion_limit) {
ABSL_ASSIGN_OR_RETURN(auto intermediate_super_box,
jumbf_internal::ParseSuperBox(unwrapped_super_box));
std::vector<ContentBox> contents;
contents.reserve(intermediate_super_box.content.size());
for (int i = 0; i < intermediate_super_box.content.size(); ++i) {
ABSL_ASSIGN_OR_RETURN(
auto content,
ParseContentBox(intermediate_super_box.content[i], recursion_limit),
_.SetPrepend() << "could not parse content box for index " << i
<< ": ");
contents.push_back(std::move(content));
}
ABSL_ASSIGN_OR_RETURN(
auto description,
FinalizeDescriptionBox(intermediate_super_box.description,
recursion_limit),
_.SetPrepend() << "could not finalize description box: ");
return SuperBox{
.description = std::move(description),
.contents = std::move(contents),
.raw_bytes = intermediate_super_box.serialized,
};
}
} // namespace
absl::StatusOr<SuperBox> ConsumeSuperBox(
absl::string_view* serialized_super_box, int recursion_limit,
bool allow_implicit_length) {
absl::string_view temp_serialized_super_box = *serialized_super_box;
ABSL_ASSIGN_OR_RETURN(
auto unwrapped_super_box,
ConsumeBox(&temp_serialized_super_box, allow_implicit_length),
_.SetPrepend() << "cannot unwrap super box: ");
ABSL_ASSIGN_OR_RETURN(auto result,
ParseSuperBox(unwrapped_super_box, recursion_limit));
*serialized_super_box = temp_serialized_super_box;
return result;
}
} // namespace jumbf