diff options
Diffstat (limited to 'source/xps/xps-zip.c')
-rw-r--r-- | source/xps/xps-zip.c | 608 |
1 files changed, 68 insertions, 540 deletions
diff --git a/source/xps/xps-zip.c b/source/xps/xps-zip.c index 0e00e918..e3d006ba 100644 --- a/source/xps/xps-zip.c +++ b/source/xps/xps-zip.c @@ -1,30 +1,26 @@ #include "mupdf/xps.h" -#include <zlib.h> - -#define ZIP_LOCAL_FILE_SIG 0x04034b50 -#define ZIP_DATA_DESC_SIG 0x08074b50 -#define ZIP_CENTRAL_DIRECTORY_SIG 0x02014b50 -#define ZIP_END_OF_CENTRAL_DIRECTORY_SIG 0x06054b50 - -#define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG 0x07064b50 -#define ZIP64_END_OF_CENTRAL_DIRECTORY_SIG 0x06064b50 -#define ZIP64_EXTRA_FIELD_SIG 0x0001 - -#define ZIP_ENCRYPTED_FLAG 0x1 - static void xps_init_document(xps_document *doc); xps_part * -xps_new_part(xps_document *doc, char *name, int size) +xps_new_part(xps_document *doc, char *name, unsigned char *data, int size) { xps_part *part; - part = fz_malloc(doc->ctx, sizeof(xps_part)); - part->name = fz_strdup(doc->ctx, name); - part->size = size; - part->data = fz_malloc(doc->ctx, size + 1); - part->data[size] = 0; /* null-terminate for xml parser */ + part = fz_malloc_struct(doc->ctx, xps_part); + fz_try(doc->ctx) + { + part->name = fz_strdup(doc->ctx, name); + part->data = data; + part->size = size; + } + fz_catch(doc->ctx) + { + fz_free(doc->ctx, part->name); + fz_free(doc->ctx, part->data); + fz_free(doc->ctx, part); + fz_rethrow(doc->ctx); + } return part; } @@ -37,552 +33,91 @@ xps_free_part(xps_document *doc, xps_part *part) fz_free(doc->ctx, part); } -static inline int getshort(fz_stream *file) -{ - int a = fz_read_byte(file); - int b = fz_read_byte(file); - return a | b << 8; -} - -static inline int getlong(fz_stream *file) -{ - int a = fz_read_byte(file); - int b = fz_read_byte(file); - int c = fz_read_byte(file); - int d = fz_read_byte(file); - return a | b << 8 | c << 16 | d << 24; -} - -static inline int getlong64(fz_stream *file) -{ - int a = getlong(file); - int b = getlong(file); - return b != 0 ? -1 : a; -} - -static void * -xps_zip_alloc_items(xps_document *doc, int items, int size) -{ - return fz_malloc_array(doc->ctx, items, size); -} - -static void -xps_zip_free(xps_document *doc, void *ptr) -{ - fz_free(doc->ctx, ptr); -} - -static int -xps_compare_entries(const void *a0, const void *b0) -{ - xps_entry *a = (xps_entry*) a0; - xps_entry *b = (xps_entry*) b0; - return xps_strcasecmp(a->name, b->name); -} - -static xps_entry * -xps_lookup_zip_entry(xps_document *doc, char *name) -{ - int l = 0; - int r = doc->zip_count - 1; - while (l <= r) - { - int m = (l + r) >> 1; - int c = xps_strcasecmp(name, doc->zip_table[m].name); - if (c < 0) - r = m - 1; - else if (c > 0) - l = m + 1; - else - return &doc->zip_table[m]; - } - return NULL; -} - -static void -xps_read_zip_entry(xps_document *doc, xps_entry *ent, unsigned char *outbuf) -{ - z_stream stream; - unsigned char *inbuf; - int sig; - int general; - int method; - int namelength, extralength; - int code; - fz_context *ctx = doc->ctx; - - fz_seek(doc->file, ent->offset, 0); - - sig = getlong(doc->file); - if (sig != ZIP_LOCAL_FILE_SIG) - { - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip local file signature (0x%x)", sig); - } - - (void) getshort(doc->file); /* version */ - general = getshort(doc->file); /* general */ - if (general & ZIP_ENCRYPTED_FLAG) - { - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "zipfile content is encrypted"); - } - - method = getshort(doc->file); - (void) getshort(doc->file); /* file time */ - (void) getshort(doc->file); /* file date */ - (void) getlong(doc->file); /* crc-32 */ - (void) getlong(doc->file); /* csize */ - (void) getlong(doc->file); /* usize */ - namelength = getshort(doc->file); - extralength = getshort(doc->file); - - fz_seek(doc->file, namelength + extralength, 1); - - if (method == 0) - { - fz_read(doc->file, outbuf, ent->usize); - } - else if (method == 8) - { - inbuf = fz_malloc(ctx, ent->csize); - - fz_read(doc->file, inbuf, ent->csize); - - memset(&stream, 0, sizeof(z_stream)); - stream.zalloc = (alloc_func) xps_zip_alloc_items; - stream.zfree = (free_func) xps_zip_free; - stream.opaque = doc; - stream.next_in = inbuf; - stream.avail_in = ent->csize; - stream.next_out = outbuf; - stream.avail_out = ent->usize; - - code = inflateInit2(&stream, -15); - if (code != Z_OK) - { - fz_free(ctx, inbuf); - fz_throw(ctx, FZ_ERROR_GENERIC, "zlib inflateInit2 error: %s", stream.msg); - } - code = inflate(&stream, Z_FINISH); - if (code != Z_STREAM_END) - { - inflateEnd(&stream); - fz_free(ctx, inbuf); - fz_throw(ctx, FZ_ERROR_GENERIC, "zlib inflate error: %s", stream.msg); - } - code = inflateEnd(&stream); - if (code != Z_OK) - { - fz_free(ctx, inbuf); - fz_throw(ctx, FZ_ERROR_GENERIC, "zlib inflateEnd error: %s", stream.msg); - } - - fz_free(ctx, inbuf); - - if (stream.avail_out > 0) - { - fz_warn(ctx, "Truncated zipfile entry found, possibly corrupt data"); - memset(stream.next_out, 0, stream.avail_out); - } - } - else - { - fz_throw(ctx, FZ_ERROR_GENERIC, "unknown compression method (%d)", method); - } -} - -/* - * Read the central directory in a zip file. - */ - -static void -xps_read_zip_dir(xps_document *doc, int start_offset) -{ - int sig; - int offset, count; - int namesize, metasize, commentsize; - int i; - - fz_seek(doc->file, start_offset, 0); - - sig = getlong(doc->file); - if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG) - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip end of central directory signature (0x%x)", sig); - - (void) getshort(doc->file); /* this disk */ - (void) getshort(doc->file); /* start disk */ - (void) getshort(doc->file); /* entries in this disk */ - count = getshort(doc->file); /* entries in central directory disk */ - (void) getlong(doc->file); /* size of central directory */ - offset = getlong(doc->file); /* offset to central directory */ - - /* ZIP64 */ - if (count == 0xFFFF || offset == 0xFFFFFFFF) - { - int offset64, count64; - - fz_seek(doc->file, start_offset - 20, 0); - - sig = getlong(doc->file); - if (sig != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG) - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip64 end of central directory locator signature (0x%x)", sig); - - (void) getlong(doc->file); /* start disk */ - offset64 = getlong64(doc->file); /* offset to end of central directory record */ - if (offset64 < 0) - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "zip64 files larger than 2 GB aren't supported"); - - fz_seek(doc->file, offset64, 0); - - sig = getlong(doc->file); - if (sig != ZIP64_END_OF_CENTRAL_DIRECTORY_SIG) - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip64 end of central directory signature (0x%x)", sig); - - (void) getlong64(doc->file); /* size of record */ - (void) getshort(doc->file); /* version made by */ - (void) getshort(doc->file); /* version to extract */ - (void) getlong(doc->file); /* disk number */ - (void) getlong(doc->file); /* disk number start */ - count64 = getlong64(doc->file); /* entries in central directory disk */ - (void) getlong64(doc->file); /* entries in central directory */ - (void) getlong64(doc->file); /* size of central directory */ - offset64 = getlong64(doc->file); /* offset to central directory */ - - if (count == 0xFFFF) - count = count64; - if (offset == 0xFFFFFFFF) - offset = offset64; - if (count < 0 || offset < 0) - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "zip64 files larger than 2 GB aren't supported"); - } - - doc->zip_table = fz_malloc_array(doc->ctx, count, sizeof(xps_entry)); - memset(doc->zip_table, 0, count * sizeof(xps_entry)); - doc->zip_count = count; - - fz_seek(doc->file, offset, 0); - - for (i = 0; i < count; i++) - { - sig = getlong(doc->file); - if (sig != ZIP_CENTRAL_DIRECTORY_SIG) - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip central directory signature (0x%x)", sig); - - (void) getshort(doc->file); /* version made by */ - (void) getshort(doc->file); /* version to extract */ - (void) getshort(doc->file); /* general */ - (void) getshort(doc->file); /* method */ - (void) getshort(doc->file); /* last mod file time */ - (void) getshort(doc->file); /* last mod file date */ - (void) getlong(doc->file); /* crc-32 */ - doc->zip_table[i].csize = getlong(doc->file); - doc->zip_table[i].usize = getlong(doc->file); - namesize = getshort(doc->file); - metasize = getshort(doc->file); - commentsize = getshort(doc->file); - (void) getshort(doc->file); /* disk number start */ - (void) getshort(doc->file); /* int file atts */ - (void) getlong(doc->file); /* ext file atts */ - doc->zip_table[i].offset = getlong(doc->file); - - doc->zip_table[i].name = fz_malloc(doc->ctx, namesize + 1); - fz_read(doc->file, (unsigned char*)doc->zip_table[i].name, namesize); - doc->zip_table[i].name[namesize] = 0; - - while (metasize > 0) - { - int type = getshort(doc->file); - int size = getshort(doc->file); - if (type == ZIP64_EXTRA_FIELD_SIG) - { - int sizeleft = size; - if (doc->zip_table[i].usize == 0xFFFFFFFF && sizeleft >= 8) - { - doc->zip_table[i].usize = getlong64(doc->file); - sizeleft -= 8; - } - if (doc->zip_table[i].csize == 0xFFFFFFFF && sizeleft >= 8) - { - doc->zip_table[i].csize = getlong64(doc->file); - sizeleft -= 8; - } - if (doc->zip_table[i].offset == 0xFFFFFFFF && sizeleft >= 8) - { - doc->zip_table[i].offset = getlong64(doc->file); - sizeleft -= 8; - } - fz_seek(doc->file, sizeleft - size, 1); - } - fz_seek(doc->file, size, 1); - metasize -= 4 + size; - } - if (doc->zip_table[i].usize < 0 || doc->zip_table[i].csize < 0 || doc->zip_table[i].offset < 0) - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "zip64 files larger than 2 GB aren't supported"); - - fz_seek(doc->file, commentsize, 1); - } - - qsort(doc->zip_table, count, sizeof(xps_entry), xps_compare_entries); -} - -static void -xps_find_and_read_zip_dir(xps_document *doc) -{ - unsigned char buf[512]; - int file_size, back, maxback; - int i, n; - fz_context *ctx = doc->ctx; - - fz_seek(doc->file, 0, SEEK_END); - file_size = fz_tell(doc->file); - - maxback = fz_mini(file_size, 0xFFFF + sizeof buf); - back = fz_mini(maxback, sizeof buf); - - while (back < maxback) - { - fz_seek(doc->file, file_size - back, 0); - n = fz_read(doc->file, buf, sizeof buf); - for (i = n - 4; i > 0; i--) - { - if (!memcmp(buf + i, "PK\5\6", 4)) - { - xps_read_zip_dir(doc, file_size - back + i); - return; - } - } - - back += sizeof buf - 4; - } - - fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find end of central directory"); -} - /* * Read and interleave split parts from a ZIP file. */ -static xps_part * -xps_read_zip_part(xps_document *doc, char *partname) +xps_part * +xps_read_part(xps_document *doc, char *partname) { - char buf[2048]; - xps_entry *ent; - xps_part *part; - int count, size, offset, i; + fz_context *ctx = doc->ctx; + fz_archive *zip = doc->zip; + fz_buffer *buf, *tmp; + char path[2048]; + unsigned char *data; + int size; + int count; char *name; - int seen_last = 0; + int seen_last; name = partname; if (name[0] == '/') name ++; /* All in one piece */ - ent = xps_lookup_zip_entry(doc, name); - if (ent) - { - part = xps_new_part(doc, partname, ent->usize); - fz_try(doc->ctx) - { - xps_read_zip_entry(doc, ent, part->data); - } - fz_catch(doc->ctx) - { - xps_free_part(doc, part); - fz_rethrow(doc->ctx); - } - return part; - } - - /* Count the number of pieces and their total size */ - count = 0; - size = 0; - while (!seen_last) + if (fz_has_archive_entry(ctx, zip, name)) { - sprintf(buf, "%s/[%d].piece", name, count); - ent = xps_lookup_zip_entry(doc, buf); - if (!ent) - { - sprintf(buf, "%s/[%d].last.piece", name, count); - ent = xps_lookup_zip_entry(doc, buf); - seen_last = (ent != NULL); - } - if (!ent) - break; - count ++; - size += ent->usize; + buf = fz_read_archive_entry(ctx, zip, name); } - if (!seen_last) - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find all pieces for part '%s'", partname); - /* Inflate the pieces */ - if (count) + /* Assemble all the pieces */ + else { - part = xps_new_part(doc, partname, size); - offset = 0; - for (i = 0; i < count; i++) + buf = fz_new_buffer(ctx, 512); + seen_last = 0; + for (count = 0; !seen_last; ++count) { - if (i < count - 1) - sprintf(buf, "%s/[%d].piece", name, i); - else - sprintf(buf, "%s/[%d].last.piece", name, i); - ent = xps_lookup_zip_entry(doc, buf); - fz_try(doc->ctx) + sprintf(path, "%s/[%d].piece", name, count); + if (fz_has_archive_entry(ctx, zip, path)) { - xps_read_zip_entry(doc, ent, part->data + offset); + tmp = fz_read_archive_entry(ctx, zip, path); + fz_buffer_cat(ctx, buf, tmp); + fz_drop_buffer(ctx, tmp); } - fz_catch(doc->ctx) - { - xps_free_part(doc, part); - fz_rethrow(doc->ctx); - } - offset += ent->usize; - } - return part; - } - - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find part '%s'", partname); -} - -static int -xps_has_zip_part(xps_document *doc, char *name) -{ - char buf[2048]; - if (name[0] == '/') - name++; - if (xps_lookup_zip_entry(doc, name)) - return 1; - sprintf(buf, "%s/[0].piece", name); - if (xps_lookup_zip_entry(doc, buf)) - return 1; - sprintf(buf, "%s/[0].last.piece", name); - if (xps_lookup_zip_entry(doc, buf)) - return 1; - return 0; -} - -/* - * Read and interleave split parts from files in the directory. - */ -static xps_part * -xps_read_dir_part(xps_document *doc, char *name) -{ - char buf[2048]; - xps_part *part; - FILE *file; - int count, size, offset, i, n; - int seen_last = 0; - - fz_strlcpy(buf, doc->directory, sizeof buf); - fz_strlcat(buf, name, sizeof buf); - - /* All in one piece */ - file = fopen(buf, "rb"); - if (file) - { - fseek(file, 0, SEEK_END); - size = ftell(file); - fseek(file, 0, SEEK_SET); - part = xps_new_part(doc, name, size); - fread(part->data, 1, size, file); - fclose(file); - return part; - } - - /* Count the number of pieces and their total size */ - count = 0; - size = 0; - while (!seen_last) - { - sprintf(buf, "%s%s/[%d].piece", doc->directory, name, count); - file = fopen(buf, "rb"); - if (!file) - { - sprintf(buf, "%s%s/[%d].last.piece", doc->directory, name, count); - file = fopen(buf, "rb"); - seen_last = (file != NULL); - } - if (!file) - break; - count ++; - fseek(file, 0, SEEK_END); - size += ftell(file); - fclose(file); - } - if (!seen_last) - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find all pieces for part '%s'", name); - - /* Inflate the pieces */ - if (count) - { - part = xps_new_part(doc, name, size); - offset = 0; - for (i = 0; i < count; i++) - { - if (i < count - 1) - sprintf(buf, "%s%s/[%d].piece", doc->directory, name, i); else - sprintf(buf, "%s%s/[%d].last.piece", doc->directory, name, i); - file = fopen(buf, "rb"); - if (!file) { - xps_free_part(doc, part); - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot open file '%s'", buf); + sprintf(path, "%s/[%d].last.piece", name, count); + if (fz_has_archive_entry(ctx, zip, path)) + { + tmp = fz_read_archive_entry(ctx, zip, path); + fz_buffer_cat(ctx, buf, tmp); + fz_drop_buffer(ctx, tmp); + seen_last = 1; + } + else + { + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find all pieces for part '%s'", partname); + } } - n = fread(part->data + offset, 1, size - offset, file); - offset += n; - fclose(file); } - return part; } - fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find part '%s'", name); -} + fz_write_buffer_byte(ctx, buf, 0); /* zero-terminate */ -static int -file_exists(xps_document *doc, char *name) -{ - char buf[2048]; - FILE *file; - fz_strlcpy(buf, doc->directory, sizeof buf); - fz_strlcat(buf, name, sizeof buf); - file = fopen(buf, "rb"); - if (file) - { - fclose(file); - return 1; - } - return 0; + /* take over the data */ + data = buf->data; + size = buf->len; + fz_free(ctx, buf); + + return xps_new_part(doc, partname, data, size); } -static int -xps_has_dir_part(xps_document *doc, char *name) +int +xps_has_part(xps_document *doc, char *name) { char buf[2048]; - if (file_exists(doc, name)) + if (name[0] == '/') + name++; + if (fz_has_archive_entry(doc->ctx, doc->zip, name)) return 1; sprintf(buf, "%s/[0].piece", name); - if (file_exists(doc, buf)) + if (fz_has_archive_entry(doc->ctx, doc->zip, buf)) return 1; sprintf(buf, "%s/[0].last.piece", name); - if (file_exists(doc, buf)) + if (fz_has_archive_entry(doc->ctx, doc->zip, buf)) return 1; return 0; } -xps_part * -xps_read_part(xps_document *doc, char *partname) -{ - if (doc->directory) - return xps_read_dir_part(doc, partname); - return xps_read_zip_part(doc, partname); -} - -int -xps_has_part(xps_document *doc, char *partname) -{ - if (doc->directory) - return xps_has_dir_part(doc, partname); - return xps_has_zip_part(doc, partname); -} - static xps_document * xps_open_document_with_directory(fz_context *ctx, const char *directory) { @@ -591,7 +126,7 @@ xps_open_document_with_directory(fz_context *ctx, const char *directory) doc = fz_malloc_struct(ctx, xps_document); xps_init_document(doc); doc->ctx = ctx; - doc->directory = fz_strdup(ctx, directory); + doc->zip = fz_open_directory(ctx, directory); fz_try(ctx) { @@ -614,11 +149,10 @@ xps_open_document_with_stream(fz_context *ctx, fz_stream *file) doc = fz_malloc_struct(ctx, xps_document); xps_init_document(doc); doc->ctx = ctx; - doc->file = fz_keep_stream(file); + doc->zip = fz_open_archive_with_stream(ctx, file); fz_try(ctx) { - xps_find_and_read_zip_dir(doc); xps_read_page_list(doc); } fz_catch(ctx) @@ -671,17 +205,12 @@ void xps_close_document(xps_document *doc) { xps_font_cache *font, *next; - int i; if (!doc) return; - if (doc->file) - fz_close(doc->file); - - for (i = 0; i < doc->zip_count; i++) - fz_free(doc->ctx, doc->zip_table[i].name); - fz_free(doc->ctx, doc->zip_table); + if (doc->zip) + fz_close_archive(doc->ctx, doc->zip); font = doc->font_table; while (font) @@ -696,7 +225,6 @@ xps_close_document(xps_document *doc) xps_free_page_list(doc); fz_free(doc->ctx, doc->start_part); - fz_free(doc->ctx, doc->directory); fz_free(doc->ctx, doc); } @@ -717,7 +245,7 @@ static void xps_rebind(xps_document *doc, fz_context *ctx) { doc->ctx = ctx; - fz_rebind_stream(doc->file, ctx); + fz_rebind_archive(doc->zip, ctx); fz_rebind_device(doc->dev, ctx); } |