#include <stdlib.h>
#include "zipint.h"
static void _zip_file_attributes_from_dirent(zip_file_attributes_t *attributes, zip_dirent_t *de);
ZIP_EXTERN zip_source_t *zip_source_zip_file(zip_t* za, zip_t *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_int64_t len, const char *password) {
return zip_source_zip_file_create(srcza, srcidx, flags, start, len, password, &za->error);
}
ZIP_EXTERN zip_source_t *zip_source_zip_file_create(zip_t *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_int64_t len, const char *password, zip_error_t *error) {
zip_source_t *src, *s2;
zip_stat_t st;
zip_file_attributes_t attributes;
zip_dirent_t *de;
bool partial_data, needs_crc, encrypted, needs_decrypt, compressed, needs_decompress, changed_data, have_size, have_comp_size;
zip_flags_t stat_flags;
zip_int64_t data_len;
bool take_ownership = false;
bool empty_data = false;
if (srcza == NULL || srcidx >= srcza->nentry || len < -1) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}
if (flags & ZIP_FL_ENCRYPTED) {
flags |= ZIP_FL_COMPRESSED;
}
changed_data = false;
if ((flags & ZIP_FL_UNCHANGED) == 0) {
zip_entry_t *entry = srcza->entry + srcidx;
if (ZIP_ENTRY_DATA_CHANGED(entry)) {
if ((flags & ZIP_FL_COMPRESSED) || !zip_source_supports_reopen(entry->source)) {
zip_error_set(error, ZIP_ER_CHANGED, 0);
return NULL;
}
changed_data = true;
}
else if (entry->deleted) {
zip_error_set(error, ZIP_ER_CHANGED, 0);
return NULL;
}
}
stat_flags = flags;
if (!changed_data) {
stat_flags |= ZIP_FL_UNCHANGED;
}
if (zip_stat_index(srcza, srcidx, stat_flags, &st) < 0) {
zip_error_set(error, ZIP_ER_INTERNAL, 0);
return NULL;
}
if ((start > 0 || len >= 0) && (flags & ZIP_FL_COMPRESSED)) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}
have_size = (st.valid & ZIP_STAT_SIZE) != 0;
if (len >= 0 && ((start > 0 && start + len < start) || (have_size && start + len > st.size))) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}
if (len == -1) {
if (have_size) {
if (st.size - start > ZIP_INT64_MAX) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}
data_len = (zip_int64_t)(st.size - start);
}
else {
data_len = -1;
}
}
else {
data_len = len;
}
if (have_size) {
partial_data = (zip_uint64_t)(data_len) < st.size;
}
else {
partial_data = true;
}
encrypted = (st.valid & ZIP_STAT_ENCRYPTION_METHOD) && (st.encryption_method != ZIP_EM_NONE);
needs_decrypt = ((flags & ZIP_FL_ENCRYPTED) == 0) && encrypted;
compressed = (st.valid & ZIP_STAT_COMP_METHOD) && (st.comp_method != ZIP_CM_STORE);
needs_decompress = ((flags & ZIP_FL_COMPRESSED) == 0) && compressed;
needs_crc = ((flags & ZIP_FL_COMPRESSED) == 0 || !compressed) && !partial_data && (st.valid & ZIP_STAT_CRC) != 0;
if (needs_decrypt) {
if (password == NULL) {
password = srcza->default_password;
}
if (password == NULL) {
zip_error_set(error, ZIP_ER_NOPASSWD, 0);
return NULL;
}
}
if ((de = _zip_get_dirent(srcza, srcidx, flags, error)) == NULL) {
return NULL;
}
_zip_file_attributes_from_dirent(&attributes, de);
have_comp_size = (st.valid & ZIP_STAT_COMP_SIZE) != 0;
if (needs_decrypt || needs_decompress) {
empty_data = (have_comp_size && st.comp_size == 0);
}
else {
empty_data = (have_size && st.size == 0);
}
if (empty_data) {
src = zip_source_buffer_with_attributes_create(NULL, 0, 0, &attributes, error);
}
else {
src = NULL;
}
take_ownership = src != NULL;
changed_data = changed_data || (src != NULL);
if (partial_data && !needs_decrypt && !needs_decompress) {
struct zip_stat st2;
zip_t *source_archive;
zip_uint64_t source_index;
if (changed_data) {
if (src == NULL) {
src = srcza->entry[srcidx].source;
}
source_archive = NULL;
source_index = 0;
}
else {
src = srcza->src;
source_archive = srcza;
source_index = srcidx;
}
st2.comp_method = ZIP_CM_STORE;
st2.valid = ZIP_STAT_COMP_METHOD;
if (data_len >= 0) {
st2.size = (zip_uint64_t)data_len;
st2.comp_size = (zip_uint64_t)data_len;
st2.valid |= ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE;
}
if (st.valid & ZIP_STAT_MTIME) {
st2.mtime = st.mtime;
st2.valid |= ZIP_STAT_MTIME;
}
if ((src = _zip_source_window_new(src, start, data_len, &st2, ZIP_STAT_NAME, &attributes, &de->last_mod, source_archive, source_index, take_ownership, error)) == NULL) {
return NULL;
}
}
else if (!changed_data) {
if (st.comp_size > ZIP_INT64_MAX) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}
if ((src = _zip_source_window_new(srcza->src, 0, (zip_int64_t)st.comp_size, &st, ZIP_STAT_NAME, &attributes, &de->last_mod, srcza, srcidx, take_ownership, error)) == NULL) {
return NULL;
}
}
else {
if (src == NULL) {
src = srcza->entry[srcidx].source;
}
if ((src = _zip_source_window_new(src, 0, data_len, &st, ZIP_STAT_NAME, &attributes, &de->last_mod, NULL, 0, take_ownership, error)) == NULL) {
return NULL;
}
}
if (_zip_source_set_source_archive(src, srcza) < 0) {
zip_source_free(src);
return NULL;
}
if (needs_decrypt) {
zip_encryption_implementation enc_impl;
if ((enc_impl = _zip_get_encryption_implementation(st.encryption_method, ZIP_CODEC_DECODE)) == NULL) {
zip_source_free(src);
zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0);
return NULL;
}
s2 = enc_impl(srcza, src, st.encryption_method, 0, password);
if (s2 == NULL) {
zip_source_free(src);
return NULL;
}
src = s2;
}
if (needs_decompress) {
s2 = zip_source_decompress(srcza, src, st.comp_method);
if (s2 == NULL) {
zip_source_free(src);
return NULL;
}
src = s2;
}
if (needs_crc) {
s2 = zip_source_crc_create(src, 1, error);
if (s2 == NULL) {
zip_source_free(src);
return NULL;
}
src = s2;
}
if (partial_data && (needs_decrypt || needs_decompress)) {
zip_stat_t st2;
zip_stat_init(&st2);
if (data_len >= 0) {
st2.valid = ZIP_STAT_SIZE;
st2.size = (zip_uint64_t)data_len;
}
s2 = _zip_source_window_new(src, start, data_len, &st2, ZIP_STAT_NAME, NULL, NULL, NULL, 0, true, error);
if (s2 == NULL) {
zip_source_free(src);
return NULL;
}
src = s2;
}
return src;
}
static void
_zip_file_attributes_from_dirent(zip_file_attributes_t *attributes, zip_dirent_t *de) {
zip_file_attributes_init(attributes);
attributes->valid = ZIP_FILE_ATTRIBUTES_ASCII | ZIP_FILE_ATTRIBUTES_HOST_SYSTEM | ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS;
attributes->ascii = de->int_attrib & 1;
attributes->host_system = de->version_madeby >> 8;
attributes->external_file_attributes = de->ext_attrib;
attributes->general_purpose_bit_flags = de->bitflags;
attributes->general_purpose_bit_mask = ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK;
}