Files
ZipArchive/SSZipArchive/minizip/mz_unzip.c
T
2017-10-07 00:50:57 +08:00

1049 lines
35 KiB
C

/* unzip.c -- Zip manipulation
Version 2.0.0, October 4th, 2017
part of the MiniZip project
Copyright (C) 2010-2017 Nathan Moinvaziri
Modifications for AES, PKWARE disk spanning
https://github.com/nmoinvaz/minizip
Copyright (C) 2009-2010 Mathias Svensson
Modifications for Zip64 support on both zip and unzip
http://result42.com
Copyright (C) 2007-2008 Even Rouault
Modifications of Unzip for Zip64
Copyright (C) 1998-2010 Gilles Vollant
http://www.winimage.com/zLibDll/minizip.html
This program is distributed under the terms of the same license as zlib.
See the accompanying LICENSE file for the full text of the license.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include "zlib.h"
#include "mz_error.h"
#include "mz_strm.h"
#ifdef HAVE_AES
#include "mz_strm_aes.h"
#endif
#ifdef HAVE_BZIP2
#include "mz_strm_bzip.h"
#endif
#ifndef NOUNCRYPT
# include "mz_strm_crypt.h"
#endif
#ifdef HAVE_LZMA
#include "mz_strm_lzma.h"
#endif
#include "mz_strm_mem.h"
#include "mz_strm_zlib.h"
#include "mz_unzip.h"
/***************************************************************************/
#define DISKHEADERMAGIC (0x08074b50)
#define LOCALHEADERMAGIC (0x04034b50)
#define CENTRALHEADERMAGIC (0x02014b50)
#define ENDHEADERMAGIC (0x06054b50)
#define ZIP64ENDHEADERMAGIC (0x06064b50)
#define ZIP64ENDLOCHEADERMAGIC (0x07064b50)
#define SIZECENTRALDIRITEM (0x2e)
#define SIZECENTRALHEADERLOCATOR (0x14)
#define SIZEZIPLOCALHEADER (0x1e)
#ifndef BUFREADCOMMENT
# define BUFREADCOMMENT (0x400)
#endif
/***************************************************************************/
// Contains internal information about the zip file
typedef struct mz_unzip_s
{
mz_unzip_global global_info; // public global information
mz_unzip_file file_info; // public file information
void *stream; // main stream
void *compress_stream; // compression stream
void *crc32_stream; // crc32 stream
void *crypt_stream; // encryption stream
void *file_info_stream; // memory stream for storing file info
uint64_t byte_before_the_zipfile; // byte before the zip file, (>0 for sfx)
uint64_t num_file; // number of the current file in the zip file
uint64_t pos_in_central_dir; // pos of the current file in the central dir
uint64_t central_pos; // position of the beginning of the central dir
uint32_t number_disk; // number of the current disk, used for spanning ZIP
uint64_t size_central_dir; // size of the central directory
uint64_t offset_central_dir; // offset of start of central directory with
// respect to the starting disk number
uint64_t pos_in_zipfile; // position in byte on the zip file, for fseek
uint8_t stream_initialised; // flag set if stream structure is initialised
uint64_t stream_available; // number of byte to be decompressed
uint64_t entry_header_read; // flag about the usability of the current file
uint64_t extrafield_local_offset; // offset of the local extra field
uint16_t extrafield_local_size; // size of the local extra field
uint64_t extrafield_local_pos; // position in the local extra field in read
#ifdef HAVE_AES
uint16_t aes_version;
uint8_t aes_encryption_mode;
#endif
} mz_unzip;
/***************************************************************************/
// Locate the central directory of a zip file (at the end, just before the global comment)
static uint64_t mz_unzip_search_cd(void *stream)
{
uint8_t buf[BUFREADCOMMENT + 4];
uint64_t file_size = 0;
uint64_t back_read = 4;
uint64_t max_back = UINT16_MAX; /* maximum size of global comment */
uint64_t pos_found = 0;
uint32_t read_size = 0;
uint64_t read_pos = 0;
uint32_t i = 0;
if (mz_stream_seek(stream, 0, MZ_STREAM_SEEK_END) != 0)
return 0;
file_size = mz_stream_tell(stream);
if (max_back > file_size)
max_back = file_size;
while (back_read < max_back)
{
if (back_read + BUFREADCOMMENT > max_back)
back_read = max_back;
else
back_read += BUFREADCOMMENT;
read_pos = file_size - back_read;
read_size = ((BUFREADCOMMENT + 4) < (file_size - read_pos)) ?
(BUFREADCOMMENT + 4) : (uint32_t)(file_size - read_pos);
if (mz_stream_seek(stream, read_pos, MZ_STREAM_SEEK_SET) != MZ_OK)
break;
if (mz_stream_read(stream, buf, read_size) != read_size)
break;
for (i = read_size - 3; (i--) > 0;)
{
if (((*(buf + i)) == (ENDHEADERMAGIC & 0xff)) &&
((*(buf + i + 1)) == (ENDHEADERMAGIC >> 8 & 0xff)) &&
((*(buf + i + 2)) == (ENDHEADERMAGIC >> 16 & 0xff)) &&
((*(buf + i + 3)) == (ENDHEADERMAGIC >> 24 & 0xff)))
{
pos_found = read_pos + i;
break;
}
}
if (pos_found != 0)
break;
}
return pos_found;
}
// Locate the central directory 64 of a zip file (at the end, just before the global comment)
static uint64_t mz_unzip_search_zip64_cd(void *stream, const uint64_t endcentraloffset)
{
uint64_t offset = 0;
uint32_t value32 = 0;
// Zip64 end of central directory locator
if (mz_stream_seek(stream, endcentraloffset - SIZECENTRALHEADERLOCATOR, MZ_STREAM_SEEK_SET) != 0)
return 0;
// Read locator signature
if (mz_stream_read_uint32(stream, &value32) != MZ_OK)
return 0;
if (value32 != ZIP64ENDLOCHEADERMAGIC)
return 0;
// Number of the disk with the start of the zip64 end of central directory
if (mz_stream_read_uint32(stream, &value32) != MZ_OK)
return 0;
// Relative offset of the zip64 end of central directory record8
if (mz_stream_read_uint64(stream, &offset) != MZ_OK)
return 0;
// Total number of disks
if (mz_stream_read_uint32(stream, &value32) != MZ_OK)
return 0;
// Goto end of central directory record
if (mz_stream_seek(stream, offset, MZ_STREAM_SEEK_SET) != MZ_OK)
return 0;
// The signature
if (mz_stream_read_uint32(stream, &value32) != MZ_OK)
return 0;
if (value32 != ZIP64ENDHEADERMAGIC)
return 0;
return offset;
}
extern void* ZEXPORT mz_unzip_open(void *stream)
{
mz_unzip *unzip = NULL;
uint64_t central_pos = 0;
uint64_t central_pos64 = 0;
uint64_t number_entry_CD = 0;
uint16_t value16 = 0;
uint32_t value32 = 0;
uint64_t value64 = 0;
int16_t err = MZ_OK;
unzip = (mz_unzip*)malloc(sizeof(mz_unzip));
if (unzip == NULL)
return NULL;
memset(unzip, 0, sizeof(mz_unzip));
unzip->stream = stream;
// Search for end of central directory header
central_pos = mz_unzip_search_cd(unzip->stream);
if (central_pos)
{
if (mz_stream_seek(unzip->stream, central_pos, MZ_STREAM_SEEK_SET) != 0)
err = MZ_STREAM_ERROR;
// The signature, already checked
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK)
err = MZ_STREAM_ERROR;
// Number of this disk
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->number_disk = value16;
// Number of the disk with the start of the central directory
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->global_info.number_disk_with_CD = value16;
// Total number of entries in the central directory on this disk
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->global_info.number_entry = value16;
// Total number of entries in the central directory
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
number_entry_CD = value16;
if (number_entry_CD != unzip->global_info.number_entry)
err = MZ_FORMAT_ERROR;
// Size of the central directory
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->size_central_dir = value32;
// Offset of start of central directory with respect to the starting disk number
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->offset_central_dir = value32;
// Zipfile comment length
if (mz_stream_read_uint16(unzip->stream, &unzip->global_info.comment_size) != MZ_OK)
err = MZ_STREAM_ERROR;
}
if (err == MZ_OK)
{
// Search for Zip64 end of central directory header
central_pos64 = mz_unzip_search_zip64_cd(unzip->stream, central_pos);
if (central_pos64)
{
central_pos = central_pos64;
if (mz_stream_seek(unzip->stream, central_pos, MZ_STREAM_SEEK_SET) != 0)
err = MZ_STREAM_ERROR;
// The signature, already checked
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK)
err = MZ_STREAM_ERROR;
// Size of zip64 end of central directory record
if (mz_stream_read_uint64(unzip->stream, &value64) != MZ_OK)
err = MZ_STREAM_ERROR;
// Version made by
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
// Version needed to extract
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
// Number of this disk
if (mz_stream_read_uint32(unzip->stream, &unzip->number_disk) != MZ_OK)
err = MZ_STREAM_ERROR;
// Number of the disk with the start of the central directory
if (mz_stream_read_uint32(unzip->stream, &unzip->global_info.number_disk_with_CD) != MZ_OK)
err = MZ_STREAM_ERROR;
// Total number of entries in the central directory on this disk
if (mz_stream_read_uint64(unzip->stream, &unzip->global_info.number_entry) != MZ_OK)
err = MZ_STREAM_ERROR;
// Total number of entries in the central directory
if (mz_stream_read_uint64(unzip->stream, &number_entry_CD) != MZ_OK)
err = MZ_STREAM_ERROR;
if (number_entry_CD != unzip->global_info.number_entry)
err = MZ_FORMAT_ERROR;
// Size of the central directory
if (mz_stream_read_uint64(unzip->stream, &unzip->size_central_dir) != MZ_OK)
err = MZ_STREAM_ERROR;
// Offset of start of central directory with respect to the starting disk number
if (mz_stream_read_uint64(unzip->stream, &unzip->offset_central_dir) != MZ_OK)
err = MZ_STREAM_ERROR;
}
else if ((unzip->global_info.number_entry == UINT16_MAX) || (unzip->size_central_dir == UINT16_MAX) ||
(unzip->offset_central_dir == UINT32_MAX))
{
err = MZ_FORMAT_ERROR;
}
}
if (err == MZ_OK)
{
if (central_pos < unzip->offset_central_dir + unzip->size_central_dir)
err = MZ_FORMAT_ERROR;
}
if (err == MZ_OK)
{
// Hack for zip files that have no respect for zip64
//if ((central_pos > 0xffffffff) && (unzip->offset_central_dir < 0xffffffff))
// unzip->offset_central_dir = central_pos - unzip->size_central_dir;
unzip->byte_before_the_zipfile = central_pos - (unzip->offset_central_dir + unzip->size_central_dir);
unzip->central_pos = central_pos;
}
if (err == MZ_OK)
{
mz_stream_mem_create(&unzip->file_info_stream);
mz_stream_mem_set_grow(unzip->file_info_stream, 1);
mz_stream_mem_set_grow_size(unzip->file_info_stream, 4096);
err = mz_stream_mem_open(unzip->file_info_stream, NULL, MZ_STREAM_MODE_CREATE);
}
if (err != MZ_OK)
{
if (unzip->file_info_stream != NULL)
mz_stream_mem_delete(&unzip->file_info_stream);
free(unzip);
return NULL;
}
return unzip;
}
extern int ZEXPORT mz_unzip_close(void *handle)
{
mz_unzip *unzip = NULL;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
if (unzip->stream_initialised != 0)
mz_unzip_entry_close(handle);
mz_stream_mem_close(unzip->file_info_stream);
mz_stream_mem_delete(&unzip->file_info_stream);
free(unzip);
return MZ_OK;
}
extern int ZEXPORT mz_unzip_get_global_info(void *handle, mz_unzip_global *global_info)
{
mz_unzip *unzip = NULL;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
*global_info = unzip->global_info;
return MZ_OK;
}
extern int ZEXPORT mz_unzip_get_global_comment(void *handle, char *comment, uint16_t comment_size)
{
mz_unzip *unzip = NULL;
uint16_t bytes_to_read = comment_size;
if (handle == NULL)
return (int)MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
if (comment_size > 1)
*comment = 0;
if (bytes_to_read > unzip->global_info.comment_size)
bytes_to_read = unzip->global_info.comment_size;
if (mz_stream_seek(unzip->stream, unzip->central_pos + 22, MZ_STREAM_SEEK_SET) != MZ_OK)
return MZ_STREAM_ERROR;
if (bytes_to_read > 0)
{
if (mz_stream_read(unzip->stream, comment, bytes_to_read) != bytes_to_read)
return MZ_STREAM_ERROR;
}
if ((comment != NULL) && (comment_size > unzip->global_info.comment_size))
*(comment + unzip->global_info.comment_size) = 0;
return MZ_OK;
}
// Get info about the current file in the zip file
static int mz_unzip_entry_read_header(void *handle)
{
mz_unzip *unzip = NULL;
uint32_t magic = 0;
uint32_t extra_pos = 0;
uint16_t extra_header_id = 0;
uint16_t extra_data_size = 0;
uint16_t value16 = 0;
uint32_t value32 = 0;
uint64_t value64 = 0;
int64_t seek = 0;
int16_t err = MZ_OK;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
unzip->entry_header_read = 0;
memset(&unzip->file_info, 0, sizeof(mz_unzip_file));
if (mz_stream_seek(unzip->stream,
unzip->pos_in_central_dir + unzip->byte_before_the_zipfile, MZ_STREAM_SEEK_SET) != MZ_OK)
err = MZ_STREAM_ERROR;
// Check the magic
if (err == MZ_OK)
{
if (mz_stream_read_uint32(unzip->stream, &magic) != MZ_OK)
err = MZ_STREAM_ERROR;
else if (magic != CENTRALHEADERMAGIC)
err = MZ_FORMAT_ERROR;
}
// Read central directory header
if (mz_stream_read_uint16(unzip->stream, &unzip->file_info.version) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint16(unzip->stream, &unzip->file_info.version_needed) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint16(unzip->stream, &unzip->file_info.flag) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint16(unzip->stream, &unzip->file_info.compression_method) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint32(unzip->stream, &unzip->file_info.dos_date) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint32(unzip->stream, &unzip->file_info.crc) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->file_info.compressed_size = value32;
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->file_info.uncompressed_size = value32;
if (mz_stream_read_uint16(unzip->stream, &unzip->file_info.filename_size) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint16(unzip->stream, &unzip->file_info.extrafield_size) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint16(unzip->stream, &unzip->file_info.comment_size) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->file_info.disk_num_start = value16;
if (mz_stream_read_uint16(unzip->stream, &unzip->file_info.internal_fa) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint32(unzip->stream, &unzip->file_info.external_fa) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->file_info.disk_offset = value32;
#ifdef HAVE_AES
unzip->aes_version = 0;
unzip->aes_encryption_mode = 0;
#endif
if ((err == MZ_OK) && (unzip->file_info.filename_size > 0))
{
// Read filename in our memory stream buffer
mz_stream_mem_get_buffer(unzip->file_info_stream, (void **)&unzip->file_info.filename);
if (mz_stream_seek(unzip->file_info_stream, 0, MZ_STREAM_SEEK_SET) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_copy(unzip->file_info_stream, unzip->stream, unzip->file_info.filename_size) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_write_uint8(unzip->file_info_stream, 0) != MZ_OK)
err = MZ_STREAM_ERROR;
seek += unzip->file_info.filename_size + 1;
}
if ((err == MZ_OK) && (unzip->file_info.extrafield_size > 0))
{
mz_stream_mem_get_buffer_at(unzip->file_info_stream, seek, (void **)&unzip->file_info.extrafield);
if (mz_stream_copy(unzip->file_info_stream, unzip->stream, unzip->file_info.extrafield_size) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_write_uint8(unzip->file_info_stream, 0) != MZ_OK)
err = MZ_STREAM_ERROR;
// Seek back and parse the extra field
if (mz_stream_seek(unzip->file_info_stream, seek, MZ_STREAM_SEEK_SET) != MZ_OK)
err = MZ_STREAM_ERROR;
seek += unzip->file_info.extrafield_size + 1;
while ((err == MZ_OK) && (extra_pos < unzip->file_info.extrafield_size))
{
if (mz_stream_read_uint16(unzip->file_info_stream, &extra_header_id) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint16(unzip->file_info_stream, &extra_data_size) != MZ_OK)
err = MZ_STREAM_ERROR;
// ZIP64 extra field
if (extra_header_id == 0x0001)
{
if (unzip->file_info.uncompressed_size == UINT32_MAX)
{
if (mz_stream_read_uint64(unzip->file_info_stream, &unzip->file_info.uncompressed_size) != MZ_OK)
err = MZ_STREAM_ERROR;
}
if (unzip->file_info.compressed_size == UINT32_MAX)
{
if (mz_stream_read_uint64(unzip->file_info_stream, &unzip->file_info.compressed_size) != MZ_OK)
err = MZ_STREAM_ERROR;
}
if (unzip->file_info.disk_offset == UINT32_MAX)
{
// Relative Header offset
if (mz_stream_read_uint64(unzip->file_info_stream, &value64) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->file_info.disk_offset = value64;
}
if (unzip->file_info.disk_num_start == UINT32_MAX)
{
// Disk Start Number
if (mz_stream_read_uint32(unzip->file_info_stream, &unzip->file_info.disk_num_start) != MZ_OK)
err = MZ_STREAM_ERROR;
}
}
#ifdef HAVE_AES
// AES extra field
else if (extra_header_id == 0x9901)
{
uint8_t value8 = 0;
// Verify version info
if (mz_stream_read_uint16(unzip->file_info_stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
// Support AE-1 and AE-2
if (value16 != 1 && value16 != 2)
err = MZ_FORMAT_ERROR;
unzip->aes_version = value16;
if (mz_stream_read_uint8(unzip->file_info_stream, &value8) != MZ_OK)
err = MZ_STREAM_ERROR;
if ((char)value8 != 'A')
err = MZ_FORMAT_ERROR;
if (mz_stream_read_uint8(unzip->file_info_stream, &value8) != MZ_OK)
err = MZ_STREAM_ERROR;
if ((char)value8 != 'E')
err = MZ_FORMAT_ERROR;
// Get AES encryption strength and actual compression method
if (mz_stream_read_uint8(unzip->file_info_stream, &value8) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->aes_encryption_mode = value8;
if (mz_stream_read_uint16(unzip->file_info_stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
unzip->file_info.compression_method = value16;
}
#endif
else
{
if (mz_stream_seek(unzip->file_info_stream, extra_data_size, MZ_STREAM_SEEK_CUR) != MZ_OK)
err = MZ_STREAM_ERROR;
}
extra_pos += 4 + extra_data_size;
}
}
if ((err == MZ_OK) && (unzip->file_info.comment_size > 0))
{
mz_stream_mem_get_buffer_at(unzip->file_info_stream, seek, (void **)&unzip->file_info.comment);
if (mz_stream_copy(unzip->file_info_stream, unzip->stream, unzip->file_info.comment_size) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_write_uint8(unzip->file_info_stream, 0) != MZ_OK)
err = MZ_STREAM_ERROR;
}
if (err == MZ_OK)
{
unzip->entry_header_read = 1;
unzip->stream_available = unzip->file_info.uncompressed_size;
}
return err;
}
// Read the local header of the current zip file. Check the coherency of the local header and info in the
// end of central directory about this file store in extrainfo_size the size of extra info in local header
// (filename and size of extra field data)
static int mz_unzip_entry_check_header(mz_unzip *unzip, uint32_t *extrainfo_size, uint64_t *extrafield_local_offset,
uint16_t *extrafield_local_size)
{
uint32_t magic = 0;
uint16_t value16 = 0;
uint32_t value32 = 0;
uint32_t flags = 0;
uint16_t filename_size = 0;
uint16_t extrafield_size = 0;
int err = MZ_OK;
if (extrainfo_size == NULL)
return MZ_PARAM_ERROR;
*extrainfo_size = 0;
if (extrafield_local_offset == NULL)
return MZ_PARAM_ERROR;
*extrafield_local_offset = 0;
if (extrafield_local_size == NULL)
return MZ_PARAM_ERROR;
*extrafield_local_size = 0;
if (err != MZ_OK)
return err;
if (mz_stream_seek(unzip->stream, unzip->file_info.disk_offset +
unzip->byte_before_the_zipfile, MZ_STREAM_SEEK_SET) != MZ_OK)
return MZ_STREAM_ERROR;
if (err == MZ_OK)
{
if (mz_stream_read_uint32(unzip->stream, &magic) != MZ_OK)
err = MZ_STREAM_ERROR;
else if (magic != LOCALHEADERMAGIC)
err = MZ_FORMAT_ERROR;
}
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
flags = value16;
if (mz_stream_read_uint16(unzip->stream, &value16) != MZ_OK)
err = MZ_STREAM_ERROR;
else if ((err == MZ_OK) && (value16 != unzip->file_info.compression_method))
{
#ifdef HAVE_AES
if (value16 != MZ_AES_METHOD)
err = MZ_FORMAT_ERROR;
#else
err = MZ_FORMAT_ERROR;
#endif
}
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK) // date/time
err = MZ_STREAM_ERROR;
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK) // crc
err = MZ_STREAM_ERROR;
else if ((err == MZ_OK) && (value32 != unzip->file_info.crc) && ((flags & 8) == 0))
err = MZ_FORMAT_ERROR;
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK) // size compr
err = MZ_STREAM_ERROR;
else if ((value32 != UINT32_MAX) && (err == MZ_OK) && (value32 != unzip->file_info.compressed_size) && ((flags & 8) == 0))
err = MZ_FORMAT_ERROR;
if (mz_stream_read_uint32(unzip->stream, &value32) != MZ_OK) // size uncompr
err = MZ_STREAM_ERROR;
else if ((value32 != UINT32_MAX) && (err == MZ_OK) && (value32 != unzip->file_info.uncompressed_size) && ((flags & 8) == 0))
err = MZ_FORMAT_ERROR;
if (mz_stream_read_uint16(unzip->stream, &filename_size) != MZ_OK)
err = MZ_STREAM_ERROR;
else if ((err == MZ_OK) && (filename_size != unzip->file_info.filename_size))
err = MZ_FORMAT_ERROR;
*extrainfo_size += filename_size;
if (mz_stream_read_uint16(unzip->stream, &extrafield_size) != MZ_OK)
err = MZ_STREAM_ERROR;
*extrafield_local_offset = unzip->file_info.disk_offset + SIZEZIPLOCALHEADER + filename_size;
*extrafield_local_size = extrafield_size;
*extrainfo_size += extrafield_size;
return err;
}
extern int ZEXPORT mz_unzip_entry_open(void *handle, int raw, const char *password)
{
mz_unzip *unzip = NULL;
uint64_t extrafield_local_offset = 0;
uint16_t extrafield_local_size = 0;
uint32_t size_variable = 0;
int64_t max_total_in = 0;
int16_t err = MZ_OK;
#ifdef NOUNCRYPT
if (password != NULL)
return MZ_PARAM_ERROR;
#endif
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
if (unzip->entry_header_read == 0)
return MZ_PARAM_ERROR;
if ((unzip->file_info.flag & 1) && (password == NULL))
return MZ_PARAM_ERROR;
err = mz_unzip_entry_check_header(unzip, &size_variable, &extrafield_local_offset, &extrafield_local_size);
if (err != MZ_OK)
return err;
if ((unzip->file_info.compression_method != 0) &&
(unzip->file_info.compression_method != MZ_METHOD_DEFLATE))
{
#ifdef HAVE_BZIP2
if (unzip->file_info.compression_method != MZ_METHOD_BZIP2)
return MZ_FORMAT_ERROR;
#elif HAVE_LZMA
if (unzip->file_info.compression_method != MZ_METHOD_LZMA)
return MZ_FORMAT_ERROR;
#else
return MZ_FORMAT_ERROR;
#endif
}
unzip->extrafield_local_offset = extrafield_local_offset;
unzip->extrafield_local_size = extrafield_local_size;
unzip->extrafield_local_pos = 0;
unzip->byte_before_the_zipfile = 0;
if (unzip->number_disk == unzip->global_info.number_disk_with_CD)
unzip->byte_before_the_zipfile = unzip->byte_before_the_zipfile;
unzip->pos_in_zipfile = unzip->file_info.disk_offset +
SIZEZIPLOCALHEADER + size_variable;
if (err == MZ_OK)
{
if (mz_stream_seek(unzip->stream,
unzip->pos_in_zipfile + unzip->byte_before_the_zipfile, MZ_STREAM_SEEK_SET) != MZ_OK)
err = MZ_INTERNAL_ERROR;
}
max_total_in = unzip->file_info.compressed_size;
#ifndef NOUNCRYPT
if (unzip->file_info.flag & 1)
{
#ifdef HAVE_AES
if (unzip->aes_version > 0)
{
mz_stream_aes_create(&unzip->crypt_stream);
mz_stream_aes_set_password(unzip->crypt_stream, password);
mz_stream_aes_set_encryption_mode(unzip->crypt_stream, unzip->aes_encryption_mode);
max_total_in -= mz_stream_aes_get_footer_size(unzip->crypt_stream);
mz_stream_set_base(unzip->crypt_stream, unzip->stream);
if (mz_stream_open(unzip->crypt_stream, NULL, MZ_STREAM_MODE_READ) != MZ_OK)
err = MZ_INTERNAL_ERROR;
}
else
#endif
{
mz_stream_crypt_create(&unzip->crypt_stream);
mz_stream_crypt_set_password(unzip->crypt_stream, password);
mz_stream_set_base(unzip->crypt_stream, unzip->stream);
if (mz_stream_open(unzip->crypt_stream, NULL, MZ_STREAM_MODE_READ) != MZ_OK)
err = MZ_STREAM_ERROR;
}
#endif
}
if (unzip->crypt_stream == NULL)
{
mz_stream_passthru_create(&unzip->crypt_stream);
mz_stream_set_base(unzip->crypt_stream, unzip->stream);
}
max_total_in -= mz_stream_get_total_in(unzip->crypt_stream);
if (err == MZ_OK)
{
if (raw || unzip->file_info.compression_method == 0)
{
mz_stream_passthru_create(&unzip->compress_stream);
mz_stream_set_base(unzip->compress_stream, unzip->crypt_stream);
}
else if (unzip->file_info.compression_method == MZ_METHOD_DEFLATE)
{
mz_stream_zlib_create(&unzip->compress_stream);
if (unzip->file_info.flag & 1)
mz_stream_zlib_set_max_total_in(unzip->compress_stream, max_total_in);
mz_stream_set_base(unzip->compress_stream, unzip->crypt_stream);
if (mz_stream_open(unzip->compress_stream, NULL, MZ_STREAM_MODE_READ) != MZ_OK)
err = MZ_INTERNAL_ERROR;
}
#ifdef HAVE_BZIP2
else if (unzip->file_info.compression_method == MZ_METHOD_BZIP2)
{
mz_stream_bzip_create(&unzip->compress_stream);
if (unzip->file_info.flag & 1)
mz_stream_bzip_set_max_total_in(unzip->compress_stream, max_total_in);
mz_stream_set_base(unzip->compress_stream, unzip->crypt_stream);
if (mz_stream_open(unzip->compress_stream, NULL, MZ_STREAM_MODE_READ) != MZ_OK)
err = MZ_INTERNAL_ERROR;
}
#endif
#ifdef HAVE_LZMA
else if (unzip->file_info.compression_method == MZ_METHOD_LZMA)
{
mz_stream_lzma_create(&unzip->compress_stream);
if (unzip->file_info.flag & 1)
mz_stream_lzma_set_max_total_in(unzip->compress_stream, max_total_in);
mz_stream_set_base(unzip->compress_stream, unzip->crypt_stream);
if (mz_stream_open(unzip->compress_stream, NULL, MZ_STREAM_MODE_READ) != MZ_OK)
err = MZ_INTERNAL_ERROR;
}
#endif
}
if (err == MZ_OK)
{
mz_stream_crc32_create(&unzip->crc32_stream);
mz_stream_set_base(unzip->crc32_stream, unzip->compress_stream);
if (mz_stream_open(unzip->crc32_stream, NULL, MZ_STREAM_MODE_READ) != MZ_OK)
err = MZ_INTERNAL_ERROR;
}
if (err == MZ_OK)
{
unzip->stream_initialised = 1;
}
return err;
}
extern int ZEXPORT mz_unzip_entry_read(void *handle, void *buf, uint32_t len)
{
mz_unzip *unzip = NULL;
uint32_t read = 0;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
if (unzip->stream_initialised == 0)
return MZ_PARAM_ERROR;
if (len == 0)
return 0;
if (len > UINT16_MAX)
return MZ_PARAM_ERROR;
if (len > unzip->stream_available)
len = (uint32_t)unzip->stream_available;
read = mz_stream_read(unzip->crc32_stream, buf, len);
if (read > 0)
unzip->stream_available -= read;
return read;
}
extern int ZEXPORT mz_unzip_entry_get_info(void *handle, mz_unzip_file **file_info)
{
mz_unzip *unzip = NULL;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
if (unzip->entry_header_read == 0)
return MZ_PARAM_ERROR;
*file_info = &unzip->file_info;
return MZ_OK;
}
extern int ZEXPORT mz_unzip_entry_get_extrafield_local(void *handle, void *buf, uint32_t len)
{
mz_unzip *unzip = NULL;
uint64_t size_to_read = 0;
uint32_t read_now = 0;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
if (unzip->entry_header_read == 0)
return MZ_PARAM_ERROR;
size_to_read = unzip->extrafield_local_size - unzip->extrafield_local_pos;
if (buf == NULL)
return (int)size_to_read;
if (len > size_to_read)
read_now = (uint32_t)size_to_read;
else
read_now = len;
if (read_now == 0)
return 0;
if (mz_stream_seek(unzip->stream,
unzip->extrafield_local_offset + unzip->extrafield_local_pos,
MZ_STREAM_SEEK_SET) != MZ_OK)
return MZ_STREAM_ERROR;
if (mz_stream_read(unzip->stream, buf, read_now) != read_now)
return MZ_STREAM_ERROR;
return (int)read_now;
}
extern int ZEXPORT mz_unzip_entry_close(void *handle)
{
mz_unzip *unzip = NULL;
uint32_t crc = 0;
int16_t err = MZ_OK;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
if (unzip->stream_initialised == 0)
return MZ_PARAM_ERROR;
#ifdef HAVE_AES
// AES zip version AE-1 will expect a valid crc as well
if (unzip->aes_version <= 0x0001)
#endif
{
if ((unzip->stream_available == 0) && (unzip->file_info.compression_method != 0))
{
crc = mz_stream_crc32_get_value(unzip->crc32_stream);
if (crc != unzip->file_info.crc)
err = MZ_CRC_ERROR;
}
}
mz_stream_close(unzip->compress_stream);
if (unzip->crypt_stream != NULL)
{
mz_stream_set_base(unzip->crypt_stream, unzip->stream);
err = mz_stream_close(unzip->crypt_stream);
mz_stream_delete(&unzip->crypt_stream);
}
mz_stream_delete(&unzip->compress_stream);
mz_stream_crc32_delete(&unzip->crc32_stream);
unzip->stream_initialised = 0;
return err;
}
extern int ZEXPORT mz_unzip_goto_first_entry(void *handle)
{
mz_unzip *unzip = NULL;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
unzip->num_file = 0;
unzip->pos_in_central_dir = unzip->offset_central_dir;
return mz_unzip_entry_read_header(handle);
}
extern int ZEXPORT mz_unzip_goto_next_entry(void *handle)
{
mz_unzip *unzip = NULL;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
if (unzip->entry_header_read == 0)
return MZ_END_OF_LIST;
if (unzip->global_info.number_entry != UINT16_MAX)
{
if (unzip->num_file + 1 == unzip->global_info.number_entry)
return MZ_END_OF_LIST;
}
unzip->num_file += 1;
unzip->pos_in_central_dir += SIZECENTRALDIRITEM + unzip->file_info.filename_size +
unzip->file_info.extrafield_size + unzip->file_info.comment_size;
return mz_unzip_entry_read_header(handle);
}
extern int ZEXPORT mz_unzip_locate_entry(void *handle, const char *filename, mz_filename_compare_cb filename_compare_cb)
{
mz_unzip *unzip = NULL;
int16_t err = MZ_OK;
if (handle == NULL)
return MZ_PARAM_ERROR;
unzip = (mz_unzip*)handle;
if (unzip->entry_header_read == 0)
return MZ_END_OF_LIST;
err = mz_unzip_goto_first_entry(handle);
while (err == MZ_OK)
{
if (filename_compare_cb != NULL)
err = filename_compare_cb(handle, unzip->file_info.filename, filename);
else
err = strcmp(unzip->file_info.filename, filename);
if (err == 0)
return MZ_OK;
err = mz_unzip_goto_next_entry(handle);
}
return err;
}