| // 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. |
| // |
| |
| #ifndef THIRD_PARTY_CREDENTIO_FORMATS_ZIP_READER_H_ |
| #define THIRD_PARTY_CREDENTIO_FORMATS_ZIP_READER_H_ |
| |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/base/attributes.h" |
| #include "absl/base/nullability.h" |
| #include "absl/status/statusor.h" |
| #include "absl/types/span.h" |
| #include "formats/byte_range.h" |
| #include "riegeli/bytes/reader.h" |
| |
| namespace credentio { |
| |
| // Zip Reader for C2PA, holds onto a reference to a `riegeli::Reader` which |
| // must outlive the reader. |
| class ZipReader { |
| public: |
| // Represents a file entry in a ZIP archive, derived from the file header in |
| // the central directory. |
| struct FileEntry { |
| uint64_t central_directory_header_offset = 0; |
| uint64_t local_file_header_offset = 0; |
| std::string file_name; |
| ByteRange file_range; |
| bool has_data_descriptor = false; |
| uint16_t general_purpose_bit_flag = 0; |
| uint16_t compression_method = 0; |
| uint32_t uncompressed_size = 0; |
| uint16_t extra_field_length = 0; |
| uint16_t file_comment_length = 0; |
| |
| bool operator==(const FileEntry& other) const { |
| return central_directory_header_offset == |
| other.central_directory_header_offset && |
| local_file_header_offset == other.local_file_header_offset && |
| file_name == other.file_name && file_range == other.file_range && |
| has_data_descriptor == other.has_data_descriptor && |
| general_purpose_bit_flag == other.general_purpose_bit_flag && |
| compression_method == other.compression_method && |
| uncompressed_size == other.uncompressed_size && |
| extra_field_length == other.extra_field_length && |
| file_comment_length == other.file_comment_length; |
| } |
| }; |
| |
| static absl::StatusOr<std::unique_ptr<ZipReader>> Create( |
| riegeli::Reader* absl_nonnull input ABSL_ATTRIBUTE_LIFETIME_BOUND); |
| |
| // Sorts ZIP file entries by header offset and checks that they do not have |
| // duplicate offsets or overlapping ranges to prevent ZIP bomb conditions. |
| static absl::StatusOr<std::vector<ZipReader::FileEntry>> SortFileEntries( |
| absl::Span<const ZipReader::FileEntry> file_entries); |
| |
| // Returns the offset of the start of the central directory. |
| uint64_t central_directory_offset() const { |
| return central_directory_offset_; |
| } |
| |
| // Returns true if there is a file entry to read. |
| bool HasNext() const { return current_it_ < file_entries_.end(); }; |
| |
| // Reads the next file entry in the ZIP archive. Returns an OUT_OF_RANGE |
| // error if there are no more entries to read. |
| absl::StatusOr<FileEntry> Next(); |
| |
| private: |
| explicit ZipReader(std::vector<ZipReader::FileEntry>&& file_entries, |
| uint64_t central_directory_offset) |
| : file_entries_(std::move(file_entries)), |
| current_it_(file_entries_.begin()), |
| central_directory_offset_(central_directory_offset) {} |
| |
| // All the file entries in the ZIP archive. |
| const std::vector<ZipReader::FileEntry> file_entries_; |
| std::vector<ZipReader::FileEntry>::const_iterator current_it_; |
| |
| uint64_t central_directory_offset_; |
| }; |
| } // namespace credentio |
| |
| #endif // THIRD_PARTY_CREDENTIO_FORMATS_ZIP_READER_H_ |