| // 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 |