diff --git a/src/gmio_core/internal/zip_utils.c b/src/gmio_core/internal/zip_utils.c new file mode 100644 index 0000000..22575a9 --- /dev/null +++ b/src/gmio_core/internal/zip_utils.c @@ -0,0 +1,361 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#include "zip_utils.h" + +#include "../error.h" +#include "byte_codec.h" +#include "helper_stream.h" +#include + +/* ---------- + * Constants + * ---------- */ + +enum { + GMIO_ZIP_SIZE_LOCAL_FILE_HEADER = 4 + 5*2 + 3*4 + 2*2, + GMIO_ZIP_SIZE_CENTRAL_DIRECTORY_HEADER = 4 + 6*2 + 3*4 + 5*2 +2*4, + GMIO_ZIP64_SIZE_DATA_DESCRIPTOR = 4 + 2*8, + GMIO_ZIP_SIZE_DATA_DESCRIPTOR = 4 + 2*4, + GMIO_ZIP_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD = 4 + 4*2 + 2*4 + 2 +}; + +/* ---------- + * Internal functions + * ---------- */ + +/* See http://www.vsft.com/hal/dostime.htm */ +static uint32_t gmio_to_msdos_datetime(const struct tm* datetime) +{ + const uint32_t msdos_datetime = + /* Time */ + (datetime->tm_sec) /* bit 0-4 */ + | (datetime->tm_min << 5) /* bit 5-10 */ + | (datetime->tm_hour << 11) /* bit 11-15 */ + /* Date */ + | (datetime->tm_mday << 16) /* bit 16-20 */ + | ((datetime->tm_mon + 1) << 21) /* bit 21-24 */ + | ((datetime->tm_year - 80) << 25); /* bit 21-24 */ + return msdos_datetime; +} + +/* Returns a non-null datetime, if arg is NULL it returns current gmtime() */ +static const struct tm* gmio_nonnull_datetime(const struct tm* datetime) +{ + if (datetime == NULL) { + const time_t current_time = time(NULL); + return gmtime(¤t_time); /* UTC time */ + } + return datetime; +} + +#ifdef GMIO_HAVE_INT64_TYPE +/* Encodes little-endian 64b val in buffer and returns advanced buffer pos */ +static uint8_t* gmio_adv_encode_uint64_le(uint64_t val, uint8_t* bytes) +{ + gmio_encode_uint64_le(val, bytes); + return bytes + 8; +} +#endif + +/* Encodes little-endian 32b val in buffer and returns advanced buffer pos */ +static uint8_t* gmio_adv_encode_uint32_le(uint32_t val, uint8_t* bytes) +{ + gmio_encode_uint32_le(val, bytes); + return bytes + 4; +} + +/* Encodes little-endian 16b val in buffer and returns advanced buffer pos */ +static uint8_t* gmio_adv_encode_uint16_le(uint16_t val, uint8_t* bytes) +{ + gmio_encode_uint16_le(val, bytes); + return bytes + 2; +} + +/* Helper to facilitate return from gmio_zip_write_xxx() API functions */ +static size_t gmio_zip_write_returnhelper( + struct gmio_stream* stream, size_t written, size_t expected, int* error) +{ + if (error != NULL) { + const bool no_error = written == expected && !gmio_stream_error(stream); + *error = no_error ? GMIO_ERROR_OK : GMIO_ERROR_STREAM; + } + return written; +} + +/* ---------- + * API functions + * ---------- */ + +size_t gmio_zip_write_local_file_header( + struct gmio_stream* stream, + const struct gmio_zip_local_file_header* info, + int* error) +{ + uint8_t fixed_data[GMIO_ZIP_SIZE_LOCAL_FILE_HEADER]; + uint8_t* buff = fixed_data; + + const bool use_data_descriptor = + info->general_purpose_flags & GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR; + + /* 4-bytes magic number 0x04034b50 */ + buff = gmio_adv_encode_uint32_le(0x04034b50, buff); + /* 2-bytes version needed to extract */ + buff = gmio_adv_encode_uint16_le(info->version_needed_to_extract, buff); + /* 2-bytes general purpose bit flag */ + buff = gmio_adv_encode_uint16_le(info->general_purpose_flags, buff); + /* 2-bytes compression method */ + buff = gmio_adv_encode_uint16_le(info->compress_method, buff); + /* 2-bytes last mod file time */ + /* 2-bytes last mod file date */ + const struct tm* lastmod = gmio_nonnull_datetime(info->lastmod_datetime); + buff = gmio_adv_encode_uint32_le(gmio_to_msdos_datetime(lastmod), buff); + /* 4-bytes crc-32 */ + buff = gmio_adv_encode_uint32_le( + use_data_descriptor ? 0 : info->crc32, buff); + /* 4-bytes compressed size */ + buff = gmio_adv_encode_uint32_le( + use_data_descriptor ? 0 : info->compressed_size, buff); + /* 4-bytes uncompressed size */ + buff = gmio_adv_encode_uint32_le( + use_data_descriptor ? 0 : info->uncompressed_size, buff); + /* 2-bytes file name length */ + buff = gmio_adv_encode_uint16_le(info->filename_len, buff); + /* 2-bytes extra field length */ + buff = gmio_adv_encode_uint16_le(info->extrafield_len, buff); + + /* Write to stream */ + const size_t expected_written_len = + sizeof(fixed_data) + info->filename_len + info->extrafield_len; + size_t written_len = 0; + written_len += + gmio_stream_write_bytes(stream, fixed_data, sizeof(fixed_data)); + written_len += + gmio_stream_write_bytes(stream, info->filename, info->filename_len); + written_len += + gmio_stream_write_bytes(stream, info->extrafield, info->extrafield_len); + return gmio_zip_write_returnhelper( + stream, written_len, expected_written_len, error); +} + +size_t gmio_zip_write_data_descriptor( + struct gmio_stream *stream, + const struct gmio_zip_data_descriptor *info, + int* error) +{ + const size_t fixed_data_len = + info->use_zip64 ? + GMIO_ZIP64_SIZE_DATA_DESCRIPTOR : + GMIO_ZIP_SIZE_DATA_DESCRIPTOR; + uint8_t fixed_data[GMIO_ZIP64_SIZE_DATA_DESCRIPTOR]; + uint8_t* buff = fixed_data; + + /* 4-bytes crc-32 */ + buff = gmio_adv_encode_uint32_le(info->crc32, buff); + /* Compressed size and uncompressed size (4 or 8 bytes) */ + if (info->use_zip64) { +#ifdef GMIO_HAVE_INT64_TYPE + buff = gmio_adv_encode_uint64_le(info->compressed_size, buff); + buff = gmio_adv_encode_uint64_le(info->uncompressed_size, buff); +#else + return GMIO_ERROR_UNKNOWN; /* TODO: error code */ +#endif + } + else { + if (info->compressed_size <= UINT32_MAX + && info->uncompressed_size <= UINT32_MAX) + { + buff = gmio_adv_encode_uint32_le((uint32_t)info->compressed_size, buff); + buff = gmio_adv_encode_uint32_le((uint32_t)info->uncompressed_size, buff); + } + else { + return GMIO_ERROR_UNKNOWN; /* TODO: error code */ + } + } + + /* Write to stream */ + const size_t written_len = + gmio_stream_write_bytes(stream, fixed_data, fixed_data_len); + return gmio_zip_write_returnhelper( + stream, written_len, fixed_data_len, error); +} + +size_t gmio_zip_write_central_directory_header( + struct gmio_stream *stream, + const struct gmio_zip_central_directory_header *info, + int* error) +{ + uint8_t fixed_data[GMIO_ZIP_SIZE_CENTRAL_DIRECTORY_HEADER]; + uint8_t* buff = fixed_data; + + /* 4-bytes magic number 0x02014b50 */ + buff = gmio_adv_encode_uint32_le(0x02014b50, buff); + /* 2-bytes version made by */ + buff = gmio_adv_encode_uint16_le(info->version_made_by, buff); + /* 2-bytes version needed to extract */ + buff = gmio_adv_encode_uint16_le(info->version_needed_to_extract, buff); + /* 2-bytes general purpose bit flag */ + buff = gmio_adv_encode_uint16_le(info->general_purpose_flags, buff); + /* 2-bytes compression method */ + buff = gmio_adv_encode_uint16_le(info->compress_method, buff); + /* 2-bytes last mod file time */ + /* 2-bytes last mod file date */ + const struct tm* lastmod = gmio_nonnull_datetime(info->lastmod_datetime); + buff = gmio_adv_encode_uint32_le(gmio_to_msdos_datetime(lastmod), buff); + /* 4-bytes crc-32 */ + buff = gmio_adv_encode_uint32_le(info->crc32, buff); + /* 4-bytes compressed size */ + const uint32_t compressed_size = + info->use_zip64 ? UINT32_MAX : info->compressed_size; + buff = gmio_adv_encode_uint32_le(compressed_size, buff); + /* 4-bytes uncompressed size */ + const uint32_t uncompressed_size = + info->use_zip64 ? UINT32_MAX : info->uncompressed_size; + buff = gmio_adv_encode_uint32_le(uncompressed_size, buff); + /* 2-bytes file name length */ + buff = gmio_adv_encode_uint16_le(info->filename_len, buff); + /* 2-bytes extra field length */ + buff = gmio_adv_encode_uint16_le(info->extrafield_len, buff); + /* 2-bytes file comment length */ + buff = gmio_adv_encode_uint16_le(info->filecomment_len, buff); + /* 2-bytes disk number start */ + buff = gmio_adv_encode_uint16_le(info->disk_nb_start, buff); + /* 2-bytes internal file attributes */ + buff = gmio_adv_encode_uint16_le(info->internal_file_attrs, buff); + /* 4-bytes external file attributes */ + buff = gmio_adv_encode_uint32_le(info->external_file_attrs, buff); + /* 4-bytes relative offset of local header */ + const uint32_t relative_offset_local_header = + info->use_zip64 ? UINT32_MAX : info->relative_offset_local_header; + buff = gmio_adv_encode_uint32_le(relative_offset_local_header, buff); + + /* Write to stream */ + const size_t expected_written_len = + sizeof(fixed_data) + + info->filename_len + info->extrafield_len + info->filecomment_len; + size_t written_len = 0; + written_len += + gmio_stream_write_bytes(stream, fixed_data, sizeof(fixed_data)); + written_len += + gmio_stream_write_bytes(stream, info->filename, info->filename_len); + written_len += + gmio_stream_write_bytes(stream, info->extrafield, info->extrafield_len); + written_len += + gmio_stream_write_bytes(stream, info->filecomment, info->filecomment_len); + return gmio_zip_write_returnhelper( + stream, written_len, expected_written_len, error); +} + +size_t gmio_zip64_write_extrafield_extended_info( + uint8_t *buff, + size_t buff_capacity, + const struct gmio_zip64_extrablock_extended_info *info, + int* error) +{ + if (buff_capacity < GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO) { + *error = GMIO_ERROR_INVALID_MEMBLOCK_SIZE; + return 0; + } +#ifdef GMIO_HAVE_INT64_TYPE + /* Tag */ + buff = gmio_adv_encode_uint16_le(0x0001, buff); + /* Size of the "extra" block */ + buff = gmio_adv_encode_uint16_le( + GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO - 2, buff); + /* Original uncompressed file size */ + buff = gmio_adv_encode_uint64_le(info->uncompressed_size, buff); + /* Size of compressed data */ + buff = gmio_adv_encode_uint64_le(info->compressed_size, buff); + /* Offset of local header record */ + buff = gmio_adv_encode_uint64_le(info->relative_offset_local_header, buff); + /* Number of the disk on which this file starts */ + buff = gmio_adv_encode_uint32_le(info->disk_nb_start, buff); + *error = GMIO_ERROR_OK; + return GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO; +#else + *error = GMIO_ERROR_UNKNOWN; /* TODO: error code */ + return 0; +#endif +} + +size_t gmio_zip_write_end_of_central_directory_record( + struct gmio_stream *stream, + const struct gmio_zip_end_of_central_directory_record *info, + int* error) +{ + uint8_t fixed_data[GMIO_ZIP_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD]; + uint8_t* buff = fixed_data; + + /* 4-bytes magic number 0x06054b50 */ + buff = gmio_adv_encode_uint32_le(0x06054b50, buff); + + /* 2-bytes number of this disk */ + const uint16_t disk_nb = info->use_zip64 ? UINT16_MAX : info->disk_nb; + buff = gmio_adv_encode_uint16_le(disk_nb, buff); + + /* 2-bytes number of the disk with the start of the central directory */ + const uint16_t disk_nb_with_start_of_central_dir = + info->use_zip64 ? UINT16_MAX : info->disk_nb_with_start_of_central_dir; + buff = gmio_adv_encode_uint16_le(disk_nb_with_start_of_central_dir, buff); + + /* 2-bytes total number of entries in the central directory on this disk */ + const uint16_t total_entry_count_in_central_dir_on_disk = + info->use_zip64 ? UINT16_MAX : info->total_entry_count_in_central_dir_on_disk; + buff = gmio_adv_encode_uint16_le(total_entry_count_in_central_dir_on_disk, buff); + + /* 2-bytes total number of entries in the central directory */ + const uint16_t total_entry_count_in_central_dir = + info->use_zip64 ? UINT16_MAX : info->total_entry_count_in_central_dir; + buff = gmio_adv_encode_uint16_le(total_entry_count_in_central_dir, buff); + + /* 4-bytes size of the central directory */ + const uint32_t central_dir_size = + info->use_zip64 ? UINT32_MAX : info->central_dir_size; + buff = gmio_adv_encode_uint32_le(central_dir_size, buff); + + /* 4-bytes offset of start of central directory with respect to the starting + * disk number */ + const uint32_t start_offset_central_dir_from_disk_start_nb = + info->use_zip64 ? UINT32_MAX : info->start_offset_central_dir_from_disk_start_nb; + buff = gmio_adv_encode_uint32_le(start_offset_central_dir_from_disk_start_nb, buff); + + /* 2-bytes .ZIP file comment length */ + buff = gmio_adv_encode_uint16_le(info->filecomment_len, buff); + + /* Write to stream */ + const size_t expected_written_len = + sizeof(fixed_data) + info->filecomment_len; + size_t written_len = 0; + written_len += + gmio_stream_write_bytes(stream, fixed_data, sizeof(fixed_data)); + written_len += + gmio_stream_write_bytes(stream, info->filecomment, info->filecomment_len); + return gmio_zip_write_returnhelper( + stream, written_len, expected_written_len, error); +} diff --git a/src/gmio_core/internal/zip_utils.h b/src/gmio_core/internal/zip_utils.h new file mode 100644 index 0000000..85eccf9 --- /dev/null +++ b/src/gmio_core/internal/zip_utils.h @@ -0,0 +1,284 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#ifndef GMIO_INTERNAL_ZIP_UTILS_H +#define GMIO_INTERNAL_ZIP_UTILS_H + +#include "../global.h" + +#include "../stream.h" +#include + +enum gmio_zip_compress_method { + GMIO_ZIP_COMPRESS_METHOD_NO_COMPRESSION = 0, + GMIO_ZIP_COMPRESS_METHOD_SHRUNK = 1, + GMIO_ZIP_COMPRESS_METHOD_REDUCE_FACTOR_1 = 2, + GMIO_ZIP_COMPRESS_METHOD_REDUCE_FACTOR_2 = 3, + GMIO_ZIP_COMPRESS_METHOD_REDUCE_FACTOR_3 = 4, + GMIO_ZIP_COMPRESS_METHOD_REDUCE_FACTOR_4 = 5, + GMIO_ZIP_COMPRESS_METHOD_IMPLODE = 6, + GMIO_ZIP_COMPRESS_METHOD_DEFLATE = 8, + GMIO_ZIP_COMPRESS_METHOD_DEFLATE64 = 9, + GMIO_ZIP_COMPRESS_METHOD_PKWARE_IMPLODE = 10, + GMIO_ZIP_COMPRESS_METHOD_BZIP2 = 12, + GMIO_ZIP_COMPRESS_METHOD_LZMA = 14, + GMIO_ZIP_COMPRESS_METHOD_IBM_TERSE = 18, + GMIO_ZIP_COMPRESS_METHOD_IBM_LZ77 = 19, + GMIO_ZIP_COMPRESS_METHOD_WAVPACK = 97, + GMIO_ZIP_COMPRESS_METHOD_PPMD_VERSION_I_REV_1 = 98 +}; + +enum gmio_zip_general_purpose_flag { + /*! Bit_0: if set, indicates that the file is encrypted */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_FILE_ENCRYPTED = 0x0001, + + /* For Method 6 - Imploding */ + /*! Bit_1: if set, indicates an 8K sliding dictionary was used. If clear, + * then a 4K sliding dictionary was used */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_IMPLODE_8K_SLIDING_DICT = 0x0002, + /*! Bit_2: if set, indicates 3 Shannon-Fano trees were used to encode the + * sliding dictionary output. If clear, then 2 Shannon-Fano trees + * were used */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_IMPLODE_3_SHANNON_FANO_TREES = 0x0004, + + /* For Methods 8 and 9 - Deflating */ + /*! Bit_2=0 Bit_1=0 -> Normal (-en) compression option was used */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_DEFLATE_COMPRESS_NORMAL = 0x0000, + /*! Bit_2=0 Bit_1=1 -> Maximum (-exx/-ex) compression option was used */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_DEFLATE_COMPRESS_MAX = 0x0002, + /*! Bit_2=1 Bit_1=0 -> Fast (-ef) compression option was used */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_DEFLATE_COMPRESS_FAST = 0x0004, + /*! Bit_2=1 Bit_1=1 -> Super Fast (-es) compression option was used */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_DEFLATE_COMPRESS_SUPER_FAST = 0x0006, + + /* For Method 14 - LZMA */ + /*! Bit_1: if set, indicates an end-of-stream (EOS) marker is used to mark + * the end of the compressed data stream. If clear, then an EOS + * marker is not present and the compressed data size must be known + * to extract */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_LZMA_USE_EOS_MARKER = 0x0002, + + /*! Bit_3: if set, the fields crc-32, compressed size and uncompressed size + * are set to zero in the local header. + * The correct values are put in the data descriptor immediately + * following the compressed data */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR = 0x0008, + + /*! Bit_5: if set, indicates that the file is compressed patched data. + * (Note: Requires PKZIP version 2.70 or greater) */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_COMPRESSED_PATCHED_DATA = 0x0020, + + /*! Bit_6: Strong encryption. If set, you MUST set the version needed to + * extract value to at least 50 and you MUST also set bit 0. If AES + * encryption is used, the version needed to extract value MUST be + * at least 51. */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_STRONG_ENCRYPTION = 0x0040, + + /*! Bit_11: Language encoding flag (EFS). If set, the filename and comment + * fields for this file MUST be encoded using UTF-8 */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_ENCODING_UTF8 = 0x0800, + + /*! Bit_13: Set when encrypting the Central Directory to indicate selected + * data values in the Local Header are masked to hide their actual + * values */ + GMIO_ZIP_GENERAL_PURPOSE_FLAG_MASK_LOCAL_HEADER = 0x2000 +}; + +/*! Header ID mapping defined by PKWARE */ +enum gmio_zip_pkware_headerid +{ + GMIO_ZIP_PKWARE_HEADERID_ZIP64_EXTENDED_INFO = 0x0001, + GMIO_ZIP_PKWARE_HEADERID_AV_INFO = 0x0007, + GMIO_ZIP_PKWARE_HEADERID_EXTENDED_LANGUAGE_ENCODE_DATA = 0x0008, + GMIO_ZIP_PKWARE_HEADERID_OS2 = 0x0009, + GMIO_ZIP_PKWARE_HEADERID_NTFS = 0x000A, + GMIO_ZIP_PKWARE_HEADERID_OPENVMS = 0x000C, + GMIO_ZIP_PKWARE_HEADERID_UNIX = 0x000D, + GMIO_ZIP_PKWARE_HEADERID_STREAM_AND_FORK_DESCRIPTORS = 0x000E, + GMIO_ZIP_PKWARE_HEADERID_PATCH_DESCRIPTOR = 0x000F, + GMIO_ZIP_PKWARE_HEADERID_PKCS7_X509_CERT = 0x0014, + GMIO_ZIP_PKWARE_HEADERID_X509_CERTID_FOR_FILE = 0x0015, + GMIO_ZIP_PKWARE_HEADERID_X509_CERTID_FOR_CENTRAL_DIRECTORY = 0x0016, + GMIO_ZIP_PKWARE_HEADERID_STRONG_ENCRYPTION_HEADER = 0x0017, + GMIO_ZIP_PKWARE_HEADERID_RECORD_MANAGEMENT_CONTROLS = 0x0018, + GMIO_ZIP_PKWARE_HEADERID_PKCS7_ENCRYPTION_RECIPIENT_CERT_LIST = 0x0019 +}; + +enum gmio_zip_feature_version { + GMIO_ZIP_FEATURE_VERSION_DEFAULT = 10, + GMIO_ZIP_FEATURE_VERSION_FILE_VOLUME_LABEL = 11, + GMIO_ZIP_FEATURE_VERSION_FILE_FOLDER = 20, + GMIO_ZIP_FEATURE_VERSION_FILE_COMPRESSED_DEFLATE = 20, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_PKWARE = 20, + GMIO_ZIP_FEATURE_VERSION_FILE_COMPRESSED_DEFLATE64 = 21, + GMIO_ZIP_FEATURE_VERSION_FILE_COMPRESSED_PKWARE_DCL_IMPLODE = 25, + GMIO_ZIP_FEATURE_VERSION_FILE_PATCH_DATA_SET = 27, + GMIO_ZIP_FEATURE_VERSION_FILE_ZIP64_FORMAT_EXTENSIONS = 45, + GMIO_ZIP_FEATURE_VERSION_FILE_COMPRESSED_BZIP2 = 46, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_DES = 50, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_3DES = 50, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_RC2 = 50, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_RC4 = 50, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_AES = 51, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_RC2_CORRECTED = 51, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_RC2_64_CORRECTED = 52, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_NON_OAEP_KEY_WRAP = 61, + GMIO_ZIP_FEATURE_VERSION_CENTRAL_DIRECTORY_ENCRYPTION = 62, + GMIO_ZIP_FEATURE_VERSION_FILE_COMPRESSED_LZMA = 63, + GMIO_ZIP_FEATURE_VERSION_FILE_COMPRESSED_PPMD_PLUS = 63, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_BLOWFISH = 63, + GMIO_ZIP_FEATURE_VERSION_FILE_ENCRYPTED_TWOFISH = 63 +}; + +/*! Zip local file header */ +struct gmio_zip_local_file_header { + enum gmio_zip_feature_version version_needed_to_extract; + uint16_t general_purpose_flags; + enum gmio_zip_compress_method compress_method; + const struct tm* lastmod_datetime; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + const char* filename; + const uint8_t* extrafield; + uint16_t filename_len; + uint16_t extrafield_len; +}; + +/*! Zip data descriptor */ +struct gmio_zip_data_descriptor { + bool use_zip64; + uint32_t crc32; + uintmax_t compressed_size; + uintmax_t uncompressed_size; +}; + +/*! Zip central directory header */ +struct gmio_zip_central_directory_header { + bool use_zip64; + uint16_t version_made_by; + enum gmio_zip_feature_version version_needed_to_extract; + uint16_t general_purpose_flags; + enum gmio_zip_compress_method compress_method; + const struct tm* lastmod_datetime; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_len; + uint16_t extrafield_len; + uint16_t filecomment_len; + uint16_t disk_nb_start; + uint16_t internal_file_attrs; + uint32_t external_file_attrs; + uint32_t relative_offset_local_header; + const char* filename; + const uint8_t* extrafield; + const char* filecomment; +}; + +enum { + GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO = 2*2 + 3*8 + 4 +}; + +/*! Zip64 extended info (extra field) */ +struct gmio_zip64_extrablock_extended_info { + uintmax_t compressed_size; + uintmax_t uncompressed_size; + uintmax_t relative_offset_local_header; + uint32_t disk_nb_start; +}; + +/*! Zip end of central directory record */ +struct gmio_zip_end_of_central_directory_record { + bool use_zip64; + uint16_t disk_nb; + uint16_t disk_nb_with_start_of_central_dir; + uint16_t total_entry_count_in_central_dir_on_disk; + uint16_t total_entry_count_in_central_dir; + uint32_t central_dir_size; + uint32_t start_offset_central_dir_from_disk_start_nb; + uint16_t filecomment_len; + const char* filecomment; +}; + +/*! Zip64 end of central directory record */ +struct gmio_zip64_end_of_central_directory_record { + uintmax_t remaining_record_size; /* should be 64b */ + uint16_t version_made_by; + enum gmio_zip_feature_version version_needed_to_extract; + uint32_t disk_nb; + uint32_t disk_nb_with_start_of_central_dir; + uintmax_t total_entry_count_in_central_dir_on_disk; /* should be 64b */ + uintmax_t total_entry_count_in_central_dir; /* should be 64b */ + uintmax_t central_dir_size; /* should be 64b */ + uintmax_t start_offset_central_dir_from_disk_start_nb; /* should be 64b */ + const uint8_t* extensible_data_sector; /* Reserved for use by PKWARE */ +}; + +struct gmio_zip_write_result { + int error; + size_t written_len; +}; + +/*! Writes ZIP local file header to \p stream */ +size_t gmio_zip_write_local_file_header( + struct gmio_stream* stream, + const struct gmio_zip_local_file_header* info, + int* error); + +/*! Writes ZIP data descriptor to \p stream */ +size_t gmio_zip_write_data_descriptor( + struct gmio_stream* stream, + const struct gmio_zip_data_descriptor* info, + int* error); + +/*! Writes ZIP central directory header to \p stream */ +size_t gmio_zip_write_central_directory_header( + struct gmio_stream* stream, + const struct gmio_zip_central_directory_header* info, + int* error); + +/*! Writes ZIP local file header to \p buff + * + * Returns \c GMIO_ERROR_INVALID_MEMBLOCK_SIZE if \p buff_capacity is less than + * \c GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO + */ +size_t gmio_zip64_write_extrafield_extended_info( + uint8_t* buff, + size_t buff_capacity, + const struct gmio_zip64_extrablock_extended_info* info, + int* error); + +/*! Writes ZIP end of central directory record \p stream */ +size_t gmio_zip_write_end_of_central_directory_record( + struct gmio_stream* stream, + const struct gmio_zip_end_of_central_directory_record* info, + int* error); + +#endif /* GMIO_INTERNAL_ZIP_UTILS_H */