zip_string_t * _zip_read_string(zip_buffer_t *buffer, zip_source_t *src, zip_uint16_t len, bool nulp, zip_error_t *error) { zip_uint8_t *raw; zip_string_t *s; if ((raw=_zip_read_data(buffer, src, len, nulp, error)) == NULL) return NULL; s = _zip_string_new(raw, len, ZIP_FL_ENC_GUESS, error); free(raw); return s; }
ZIP_EXTERN int zip_set_archive_comment(struct zip *za, const char *comment, zip_uint16_t len) { struct zip_string *cstr; if (ZIP_IS_RDONLY(za)) { _zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } if (len < 0 || len > MAXCOMLEN || (len > 0 && comment == NULL)) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } if (len > 0) { if ((cstr=_zip_string_new((const zip_uint8_t *)comment, len, ZIP_FL_ENC_GUESS, &za->error)) == NULL) return -1; if (_zip_guess_encoding(cstr, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_CP437) { _zip_string_free(cstr); _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } } else cstr = NULL; _zip_string_free(za->comment_changes); za->comment_changes = NULL; if (((za->comment_orig && _zip_string_equal(za->comment_orig, cstr)) || (za->comment_orig == NULL && cstr == NULL))) { _zip_string_free(cstr); za->comment_changed = 0; } else { za->comment_changes = cstr; za->comment_changed = 1; } return 0; }
int _zip_set_name(struct zip *za, zip_uint64_t idx, const char *name, zip_flags_t flags) { struct zip_entry *e; struct zip_string *str; int changed; zip_int64_t i; if (idx >= za->nentry) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } if (ZIP_IS_RDONLY(za)) { _zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } if (name && strlen(name) > 0) { /* XXX: check for string too long */ if ((str=_zip_string_new((const zip_uint8_t *)name, (zip_uint16_t)strlen(name), flags, &za->error)) == NULL) return -1; if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(str, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED) str->encoding = ZIP_ENCODING_UTF8_KNOWN; } else str = NULL; /* XXX: encoding flags needed for CP437? */ if ((i=_zip_name_locate(za, name, 0, NULL)) >= 0 && (zip_uint64_t)i != idx) { _zip_string_free(str); _zip_error_set(&za->error, ZIP_ER_EXISTS, 0); return -1; } /* no effective name change */ if (i>=0 && (zip_uint64_t)i == idx) { _zip_string_free(str); return 0; } e = za->entry+idx; if (e->changes) { _zip_string_free(e->changes->filename); e->changes->filename = NULL; e->changes->changed &= ~ZIP_DIRENT_FILENAME; } if (e->orig) changed = !_zip_string_equal(e->orig->filename, str); else changed = 1; if (changed) { if (e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } } e->changes->filename = str; e->changes->changed |= ZIP_DIRENT_FILENAME; } else { _zip_string_free(str); if (e->changes && e->changes->changed == 0) { _zip_dirent_free(e->changes); e->changes = NULL; } } return 0; }
static struct zip_cdir * _zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, const unsigned char *eocd, size_t buflen, unsigned int flags, struct zip_error *error) { struct zip_cdir *cd; const unsigned char *cdp; const unsigned char **bufp; zip_int64_t tail_len, comment_len; zip_uint64_t i, left; tail_len = buf + buflen - eocd - EOCDLEN; if (tail_len < 0) { /* not enough bytes left for comment */ _zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } /* check for end-of-central-dir magic */ if (memcmp(eocd, EOCD_MAGIC, 4) != 0) { _zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) { _zip_error_set(error, ZIP_ER_MULTIDISK, 0); return NULL; } if (eocd-EOCD64LOCLEN >= buf && memcmp(eocd-EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) cd = _zip_read_eocd64(fp, eocd-EOCD64LOCLEN, buf, buf_offset, buflen, flags, error); else cd = _zip_read_eocd(eocd, buf, buf_offset, buflen, flags, error); if (cd == NULL) return NULL; cdp = eocd + 20; comment_len = _zip_read2(&cdp); if ((zip_uint64_t)cd->offset+(zip_uint64_t)cd->size > (zip_uint64_t)buf_offset + (zip_uint64_t)(eocd-buf)) { /* cdir spans past EOCD record */ _zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (tail_len < comment_len || ((flags & ZIP_CHECKCONS) && tail_len != comment_len)) { _zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len) { if ((cd->comment=_zip_string_new(eocd+EOCDLEN, (zip_uint16_t)comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { _zip_cdir_free(cd); return NULL; } } if (cd->offset >= buf_offset) { /* if buffer already read in, use it */ cdp = buf + (cd->offset - buf_offset); bufp = &cdp; } else { /* go to start of cdir and read it entry by entry */ bufp = NULL; clearerr(fp); fseeko(fp, cd->offset, SEEK_SET); /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ if (ferror(fp) || (ftello(fp) != cd->offset)) { /* seek error or offset of cdir wrong */ if (ferror(fp)) _zip_error_set(error, ZIP_ER_SEEK, errno); else _zip_error_set(error, ZIP_ER_NOZIP, 0); _zip_cdir_free(cd); return NULL; } } left = (zip_uint64_t)cd->size; i=0; while (i<cd->nentry && left > 0) { if ((cd->entry[i].orig=_zip_dirent_new()) == NULL || (_zip_dirent_read(cd->entry[i].orig, fp, bufp, &left, 0, error)) < 0) { _zip_cdir_free(cd); return NULL; } i++; } if (i != cd->nentry || ((flags & ZIP_CHECKCONS) && left != 0)) { _zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } return cd; }
ZIP_EXTERN int zip_file_set_comment(struct zip *za, zip_uint64_t idx, const char *comment, zip_uint16_t len, zip_flags_t flags) { struct zip_entry *e; struct zip_string *cstr; struct zip_dirent *de; int changed; if ((de=_zip_get_dirent(za, idx, 0, NULL)) == NULL) return -1; if (ZIP_IS_RDONLY(za)) { _zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } if (len > 0 && comment == NULL) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } if (len > 0) { if ((cstr=_zip_string_new((const zip_uint8_t *)comment, len, flags, &za->error)) == NULL) return -1; if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(cstr, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED) cstr->encoding = ZIP_ENCODING_UTF8_KNOWN; } else cstr = NULL; e = za->entry+idx; if (e->changes) { _zip_string_free(e->changes->comment); e->changes->comment = NULL; e->changes->changed &= ~ZIP_DIRENT_COMMENT; } if (e->orig && e->orig->comment) changed = !_zip_string_equal(e->orig->comment, cstr); else changed = (cstr != NULL); if (changed) { if (e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } } e->changes->comment = cstr; e->changes->changed |= ZIP_DIRENT_COMMENT; } else { _zip_string_free(cstr); if (e->changes && e->changes->changed == 0) { _zip_dirent_free(e->changes); e->changes = NULL; } } return 0; }
int _zip_set_name(zip_t *za, zip_uint64_t idx, const char *name, zip_flags_t flags) { zip_entry_t *e; zip_string_t *str; bool same_as_orig; zip_int64_t i; const zip_uint8_t *old_name, *new_name; zip_string_t *old_str; if (idx >= za->nentry) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } if (ZIP_IS_RDONLY(za)) { zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } if (name && name[0] != '\0') { /* TODO: check for string too long */ if ((str=_zip_string_new((const zip_uint8_t *)name, (zip_uint16_t)strlen(name), flags, &za->error)) == NULL) return -1; if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(str, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED) str->encoding = ZIP_ENCODING_UTF8_KNOWN; } else str = NULL; /* TODO: encoding flags needed for CP437? */ if ((i=_zip_name_locate(za, name, 0, NULL)) >= 0 && (zip_uint64_t)i != idx) { _zip_string_free(str); zip_error_set(&za->error, ZIP_ER_EXISTS, 0); return -1; } /* no effective name change */ if (i>=0 && (zip_uint64_t)i == idx) { _zip_string_free(str); return 0; } e = za->entry+idx; if (e->orig) same_as_orig = _zip_string_equal(e->orig->filename, str); else same_as_orig = false; if (!same_as_orig && e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); _zip_string_free(str); return -1; } } if ((new_name = _zip_string_get(same_as_orig ? e->orig->filename : str, NULL, 0, &za->error)) == NULL) { _zip_string_free(str); return -1; } if (e->changes) { old_str = e->changes->filename; } else if (e->orig) { old_str = e->orig->filename; } else { old_str = NULL; } if (old_str) { if ((old_name = _zip_string_get(old_str, NULL, 0, &za->error)) == NULL) { _zip_string_free(str); return -1; } } else { old_name = NULL; } if (_zip_hash_add(za->names, new_name, idx, 0, &za->error) == false) { _zip_string_free(str); return -1; } if (old_name) { _zip_hash_delete(za->names, old_name, NULL); } if (same_as_orig) { if (e->changes) { if (e->changes->changed & ZIP_DIRENT_FILENAME) { _zip_string_free(e->changes->filename); e->changes->changed &= ~ZIP_DIRENT_FILENAME; if (e->changes->changed == 0) { _zip_dirent_free(e->changes); e->changes = NULL; } else { /* TODO: what if not cloned? can that happen? */ e->changes->filename = e->orig->filename; } } } _zip_string_free(str); } else { if (e->changes->changed & ZIP_DIRENT_FILENAME) { _zip_string_free(e->changes->filename); } e->changes->changed |= ZIP_DIRENT_FILENAME; e->changes->filename = str; } return 0; }
static zip_cdir_t * _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { zip_cdir_t *cd; zip_uint16_t comment_len; zip_uint64_t i, left; zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); zip_buffer_t *cd_buffer; if (_zip_buffer_left(buffer) < EOCDLEN) { /* not enough bytes left for comment */ zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } /* check for end-of-central-dir magic */ if (memcmp(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); } else { _zip_buffer_set_offset(buffer, eocd_offset); cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); } if (cd == NULL) return NULL; _zip_buffer_set_offset(buffer, eocd_offset + 20); comment_len = _zip_buffer_get_16(buffer); if (cd->offset + cd->size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { zip_uint64_t tail_len; _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); tail_len = _zip_buffer_left(buffer); if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len) { if ((cd->comment=_zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { _zip_cdir_free(cd); return NULL; } } } if (cd->offset >= buf_offset) { zip_uint8_t *data; /* if buffer already read in, use it */ _zip_buffer_set_offset(buffer, cd->offset - buf_offset); if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_cdir_free(cd); return NULL; } } else { cd_buffer = NULL; if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); return NULL; } /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { zip_error_set(error, ZIP_ER_NOZIP, 0); _zip_cdir_free(cd); return NULL; } } left = (zip_uint64_t)cd->size; i=0; while (left > 0) { bool grown = false; zip_int64_t entry_size; if (i == cd->nentry) { /* InfoZIP has a hack to avoid using Zip64: it stores nentries % 0x10000 */ /* This hack isn't applicable if we're using Zip64, or if there is no central directory entry following. */ if (cd->is_zip64 || left < CDENTRYSIZE) { break; } if (!_zip_cdir_grow(cd, 0x10000, error)) { _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); return NULL; } grown = true; } if ((cd->entry[i].orig=_zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) { zip_error_set(error, ZIP_ER_INCONS, 0); } _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); return NULL; } i++; left -= (zip_uint64_t)entry_size; } if (i != cd->nentry || left > 0) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } if (za->open_flags & ZIP_CHECKCONS) { bool ok; if (cd_buffer) { ok = _zip_buffer_eof(cd_buffer); } else { zip_int64_t offset = zip_source_tell(za->src); if (offset < 0) { _zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); return NULL; } ok = ((zip_uint64_t)offset == cd->offset + cd->size); } if (!ok) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } } _zip_buffer_free(cd_buffer); return cd; }
static zip_cdir_t * _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { zip_cdir_t *cd; zip_uint16_t comment_len; zip_uint64_t i, left; zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); zip_buffer_t *cd_buffer; if (_zip_buffer_left(buffer) < EOCDLEN) { /* not enough bytes left for comment */ zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } /* check for end-of-central-dir magic */ if (memcmp(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } if (_zip_buffer_get_32(buffer) != 0) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); return NULL; } if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); } else { _zip_buffer_set_offset(buffer, eocd_offset); cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); } if (cd == NULL) return NULL; _zip_buffer_set_offset(buffer, eocd_offset + 20); comment_len = _zip_buffer_get_16(buffer); if (cd->offset + cd->size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { zip_uint64_t tail_len; _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); tail_len = _zip_buffer_left(buffer); if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len) { if ((cd->comment=_zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { _zip_cdir_free(cd); return NULL; } } } if (cd->offset >= buf_offset) { zip_uint8_t *data; /* if buffer already read in, use it */ _zip_buffer_set_offset(buffer, cd->offset - buf_offset); if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_cdir_free(cd); return NULL; } } else { cd_buffer = NULL; if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); return NULL; } /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { zip_error_set(error, ZIP_ER_NOZIP, 0); _zip_cdir_free(cd); return NULL; } } left = (zip_uint64_t)cd->size; i=0; while (i<cd->nentry && left > 0) { zip_int64_t entry_size; if ((cd->entry[i].orig=_zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); return NULL; } i++; left -= (zip_uint64_t)entry_size; } if (i != cd->nentry) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } if (za->open_flags & ZIP_CHECKCONS) { bool ok; if (cd_buffer) { ok = _zip_buffer_eof(cd_buffer); } else { zip_int64_t offset = zip_source_tell(za->src); if (offset < 0) { _zip_error_set_from_source(error, za->src); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } ok = ((zip_uint64_t)offset == cd->offset + cd->size); } if (!ok) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } } _zip_buffer_free(cd_buffer); return cd; }