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