comparison mupdf-source/source/fitz/unzip.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2025 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24
25 #include <string.h>
26 #include <limits.h>
27
28 #include "z-imp.h"
29
30 #if !defined (INT32_MAX)
31 #define INT32_MAX 2147483647L
32 #endif
33
34 #define ZIP_LOCAL_FILE_SIG 0x04034b50
35 #define ZIP_DATA_DESC_SIG 0x08074b50
36 #define ZIP_CENTRAL_DIRECTORY_SIG 0x02014b50
37 #define ZIP_END_OF_CENTRAL_DIRECTORY_SIG 0x06054b50
38 #define ZIP_UP_SIG 0x7075
39
40 #define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG 0x07064b50
41 #define ZIP64_END_OF_CENTRAL_DIRECTORY_SIG 0x06064b50
42 #define ZIP64_EXTRA_FIELD_SIG 0x0001
43
44 #define ZIP_ENCRYPTED_FLAG 0x1
45
46 typedef struct
47 {
48 char *name;
49 uint64_t offset, csize, usize;
50 } zip_entry;
51
52 typedef struct
53 {
54 fz_archive super;
55
56 int count;
57 zip_entry *entries;
58 } fz_zip_archive;
59
60 static void drop_zip_archive(fz_context *ctx, fz_archive *arch)
61 {
62 fz_zip_archive *zip = (fz_zip_archive *) arch;
63 int i;
64 for (i = 0; i < zip->count; ++i)
65 fz_free(ctx, zip->entries[i].name);
66 fz_free(ctx, zip->entries);
67 }
68
69 static int ishex(char c)
70 {
71 if (c >= '0' && c <= '9')
72 return 1;
73 if (c >= 'a' && c <= 'f')
74 return 1;
75 if (c >= 'A' && c <= 'F')
76 return 1;
77 return 0;
78 }
79
80 static int unhex(char c)
81 {
82 if (c >= '0' && c <= '9')
83 return c-'0';
84 if (c >= 'a' && c <= 'f')
85 return c-'a'+10;
86 return c - 'A'+10;
87 }
88
89 /* This is to cope with the #Uffff and #Lffffff escaping scheme
90 * used by info-zip when encoding files into zipfiles on
91 * non-utf8-native platforms, like Windows. Although this isn't
92 * strictly part of the zip standard, info-zip has been doing
93 * this since 2008 at least, and it's ubiquitous. We shouldn't
94 * get '#' chars in filenames otherwise, so it's pretty safe.
95 */
96 static unsigned char *unescape(fz_context *ctx, unsigned char *name)
97 {
98 unsigned char *newname;
99 unsigned char *d;
100 unsigned char *s = name;
101 unsigned char c;
102 size_t z = 1;
103
104 /* Count the target length */
105 while ((c = *s++) != 0)
106 {
107 if (c == '#' && s[0] == 'U' &&
108 ishex(s[1]) && ishex(s[2]) && ishex(s[3]) && ishex(s[4]))
109 {
110 int uni = (unhex(s[1])<<12)+(unhex(s[2])<<8)+(unhex(s[3])<<4)+unhex(s[4]);
111
112 if (uni < 0x80)
113 {
114 /* Unlikely, cos why would it have been escaped? */
115 z++;
116 }
117 else if (uni < (1<<11))
118 {
119 z += 2;
120 }
121 else
122 {
123 z += 3;
124 }
125 s += 5;
126 }
127 else if (c == '#' && s[0] == 'L' &&
128 ishex(s[1]) && ishex(s[2]) && ishex(s[3]) && ishex(s[4]) && ishex(s[5]) && ishex(s[6]))
129 {
130 int uni = (unhex(s[1])<<20)+(unhex(s[2])<<16)+(unhex(s[3])<<12)+(unhex(s[4])<<8)+(unhex(s[5])<<4)+unhex(s[6]);
131
132 if (uni < 0x80)
133 {
134 /* Unlikely, cos why would it have been escaped? */
135 z++;
136 }
137 else if (uni < (1<<11))
138 {
139 /* Unlikely, cos why wouldn't it be #U? */
140 z += 2;
141 }
142 else if (uni < (1<<16))
143 {
144 /* Unlikely, cos why wouldn't it be #U? */
145 z += 3;
146 }
147 else if (uni <= 0x10FFFF)
148 {
149 z += 4;
150 }
151 else
152 {
153 /* Illegal char for utf-8 encoding. */
154 /* Leave escaped! */
155 z += 8;
156 }
157 s += 7;
158 }
159 else if (c >= 0x80)
160 {
161 /* Why wasn't this byte escaped? Encode it to utf-8, best we can do. */
162 z += 2;
163 }
164 else
165 z++;
166 }
167
168 newname = Memento_label(fz_malloc(ctx, z), "zip_name");
169
170 d = newname;
171 s = name;
172
173 /* Now rewrite the name */
174 while ((c = *s++) != 0)
175 {
176 if (c == '#' && s[0] == 'U' &&
177 ishex(s[1]) && ishex(s[2]) && ishex(s[3]) && ishex(s[4]))
178 {
179 int uni = (unhex(s[1])<<12)+(unhex(s[2])<<8)+(unhex(s[3])<<4)+unhex(s[4]);
180
181 if (uni < 0x80)
182 {
183 /* Unlikely, cos why would it have been escaped? */
184 *d++ = uni;
185 }
186 else if (uni < (1<<11))
187 {
188 *d++ = 0xC0+(uni>>6); /* 5 bits */
189 *d++ = 0x80+(uni & 0x3f); /* 6 bits */
190 }
191 else
192 {
193 *d++ = 0xE0+(uni>>12); /* 4 bits */
194 *d++ = 0x80+((uni>>6) & 0x3f); /* 6 bits */
195 *d++ = 0x80+(uni & 0x3f); /* 6 bits */
196 }
197 s += 5;
198 }
199 else if (c == '#' && s[0] == 'L' &&
200 ishex(s[1]) && ishex(s[2]) && ishex(s[3]) && ishex(s[4]) && ishex(s[5]) && ishex(s[6]))
201 {
202 int uni = (unhex(s[1])<<20)+(unhex(s[2])<<16)+(unhex(s[3])<<12)+(unhex(s[4])<<8)+(unhex(s[5])<<4)+unhex(s[6]);
203
204 if (uni < 0x80)
205 {
206 /* Unlikely, cos why would it have been escaped? */
207 *d++ = uni;
208 }
209 else if (uni < (1<<11))
210 {
211 /* Unlikely, cos why wouldn't it be #U? */
212 *d++ = 0xC0+(uni>>6); /* 5 bits */
213 *d++ = 0x80+(uni & 0x3f); /* 6 bits */
214 }
215 else if (uni < (1<<16))
216 {
217 /* Unlikely, cos why wouldn't it be #U? */
218 *d++ = 0xE0+(uni>>12); /* 4 bits */
219 *d++ = 0x80+((uni>>6) & 0x3f); /* 6 bits */
220 *d++ = 0x80+(uni & 0x3f); /* 6 bits */
221 }
222 else if (uni <= 0x10FFFF)
223 {
224 *d++ = 0xF0+(uni>>18); /* 3 bits */
225 *d++ = 0x80+((uni>>12) & 0x3f); /* 6 bits */
226 *d++ = 0x80+((uni>>6) & 0x3f); /* 6 bits */
227 *d++ = 0x80+(uni & 0x3f); /* 6 bits */
228 }
229 else
230 {
231 /* Illegal char for utf-8 encoding. */
232 /* Leave escaped! */
233 memcpy(d, s-1, 8);
234 d += 8;
235 }
236 s += 7;
237 }
238 else if (c >= 0x80)
239 {
240 /* Why wasn't this byte escaped? Encode it to utf-8, best we can do. */
241 *d++ = 0xC0+(c>>6); /* 5 bits */
242 *d++ = 0x80+(c & 0x3f); /* 6 bits */
243 }
244 else
245 *d++ = c;
246
247 }
248 *d = 0;
249
250 fz_free(ctx, name);
251
252 return newname;
253 }
254
255 static void read_zip_dir_imp(fz_context *ctx, fz_zip_archive *zip, int64_t start_offset)
256 {
257 fz_stream *file = zip->super.file;
258 uint32_t sig;
259 int i;
260 int namesize, metasize, commentsize;
261 uint64_t count, offset;
262 uint64_t csize, usize;
263 char *name = NULL;
264 size_t n;
265 int gp;
266 int utf8 = 0;
267
268 fz_var(name);
269
270 zip->count = 0;
271
272 fz_seek(ctx, file, start_offset, 0);
273
274 sig = fz_read_uint32_le(ctx, file);
275 if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG)
276 fz_throw(ctx, FZ_ERROR_FORMAT, "wrong zip end of central directory signature (0x%x)", sig);
277
278 (void) fz_read_uint16_le(ctx, file); /* this disk */
279 (void) fz_read_uint16_le(ctx, file); /* start disk */
280 (void) fz_read_uint16_le(ctx, file); /* entries in this disk */
281 count = fz_read_uint16_le(ctx, file); /* entries in central directory disk */
282 (void) fz_read_uint32_le(ctx, file); /* size of central directory */
283 offset = fz_read_uint32_le(ctx, file); /* offset to central directory */
284
285 /* ZIP64 */
286 if (count == 0xFFFF || offset == 0xFFFFFFFF)
287 {
288 int64_t offset64, count64;
289
290 fz_seek(ctx, file, start_offset - 20, 0);
291
292 sig = fz_read_uint32_le(ctx, file);
293 if (sig != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG)
294 fz_throw(ctx, FZ_ERROR_FORMAT, "wrong zip64 end of central directory locator signature (0x%x)", sig);
295
296 (void) fz_read_uint32_le(ctx, file); /* start disk */
297 offset64 = fz_read_uint64_le(ctx, file); /* offset to end of central directory record */
298
299 fz_seek(ctx, file, offset64, 0);
300
301 sig = fz_read_uint32_le(ctx, file);
302 if (sig != ZIP64_END_OF_CENTRAL_DIRECTORY_SIG)
303 fz_throw(ctx, FZ_ERROR_FORMAT, "wrong zip64 end of central directory signature (0x%x)", sig);
304
305 (void) fz_read_uint64_le(ctx, file); /* size of record */
306 (void) fz_read_uint16_le(ctx, file); /* version made by */
307 (void) fz_read_uint16_le(ctx, file); /* version to extract */
308 (void) fz_read_uint32_le(ctx, file); /* disk number */
309 (void) fz_read_uint32_le(ctx, file); /* disk number start */
310 count64 = fz_read_uint64_le(ctx, file); /* entries in central directory disk */
311 (void) fz_read_uint64_le(ctx, file); /* entries in central directory */
312 (void) fz_read_uint64_le(ctx, file); /* size of central directory */
313 offset64 = fz_read_uint64_le(ctx, file); /* offset to central directory */
314
315 if (count == 0xFFFF)
316 {
317 count = count64;
318 }
319 if (offset == 0xFFFFFFFF)
320 {
321 offset = offset64;
322 }
323 }
324
325 fz_seek(ctx, file, offset, 0);
326
327 fz_try(ctx)
328 {
329 if (count > INT_MAX)
330 count = INT_MAX;
331 for (i = 0; i < (int)count; i++)
332 {
333 sig = fz_read_uint32_le(ctx, file);
334 if (sig != ZIP_CENTRAL_DIRECTORY_SIG)
335 fz_throw(ctx, FZ_ERROR_FORMAT, "wrong zip central directory signature (0x%x)", sig);
336
337 (void) fz_read_uint16_le(ctx, file); /* version made by */
338 (void) fz_read_uint16_le(ctx, file); /* version to extract */
339 gp = fz_read_uint16_le(ctx, file); /* general */
340 utf8 = !!(gp & (1<<11));
341 (void) fz_read_uint16_le(ctx, file); /* method */
342 (void) fz_read_uint16_le(ctx, file); /* last mod file time */
343 (void) fz_read_uint16_le(ctx, file); /* last mod file date */
344 (void) fz_read_uint32_le(ctx, file); /* crc-32 */
345 csize = fz_read_uint32_le(ctx, file);
346 usize = fz_read_uint32_le(ctx, file);
347 namesize = fz_read_uint16_le(ctx, file);
348 metasize = fz_read_uint16_le(ctx, file);
349 commentsize = fz_read_uint16_le(ctx, file);
350 (void) fz_read_uint16_le(ctx, file); /* disk number start */
351 (void) fz_read_uint16_le(ctx, file); /* int file atts */
352 (void) fz_read_uint32_le(ctx, file); /* ext file atts */
353 offset = fz_read_uint32_le(ctx, file);
354
355 name = Memento_label(fz_malloc(ctx, namesize + 1), "zip_name");
356
357 n = fz_read(ctx, file, (unsigned char*)name, namesize);
358 if (n < (size_t)namesize)
359 fz_throw(ctx, FZ_ERROR_FORMAT, "premature end of data in zip entry name");
360 name[namesize] = '\0';
361
362 if (!utf8)
363 name = (char *)unescape(ctx, (unsigned char *)name);
364
365 while (metasize > 0)
366 {
367 int type = fz_read_uint16_le(ctx, file);
368 int size = fz_read_uint16_le(ctx, file);
369
370 if (type == ZIP64_EXTRA_FIELD_SIG)
371 {
372 int sizeleft = size;
373 if (usize == 0xFFFFFFFF && sizeleft >= 8)
374 {
375 usize = fz_read_uint64_le(ctx, file);
376 sizeleft -= 8;
377 }
378 if (csize == 0xFFFFFFFF && sizeleft >= 8)
379 {
380 csize = fz_read_uint64_le(ctx, file);
381 sizeleft -= 8;
382 }
383 if (offset == 0xFFFFFFFF && sizeleft >= 8)
384 {
385 offset = fz_read_uint64_le(ctx, file);
386 sizeleft -= 8;
387 }
388 fz_seek(ctx, file, sizeleft - size, 1);
389 }
390 if (type == ZIP_UP_SIG && size > 5)
391 {
392 int sizeleft = size - 1;
393 if (fz_read_byte(ctx, file) == 1)
394 {
395 /* Version 1 */
396 (void) fz_read_uint32(ctx, file); /* Skip the CRC */
397 sizeleft -= 4;
398 fz_free(ctx, name);
399 name = NULL;
400 name = Memento_label(fz_malloc(ctx, sizeleft + 1), "zip_name");
401 fz_read(ctx, file, (unsigned char *)name, sizeleft);
402 name[sizeleft] = 0;
403 sizeleft = 0;
404 }
405 fz_seek(ctx, file, sizeleft - size, 1);
406 }
407 fz_seek(ctx, file, size, 1);
408 metasize -= 4 + size;
409 }
410
411 if (usize > INT32_MAX || csize > INT32_MAX)
412 fz_throw(ctx, FZ_ERROR_FORMAT, "zip archive entry larger than 2 GB");
413
414 fz_seek(ctx, file, commentsize, 1);
415
416 zip->entries = Memento_label(fz_realloc_array(ctx, zip->entries, zip->count + 1, zip_entry), "zip_entries");
417
418 zip->entries[zip->count].offset = offset;
419 zip->entries[zip->count].csize = csize;
420 zip->entries[zip->count].usize = usize;
421 zip->entries[zip->count].name = name;
422 name = NULL;
423
424 zip->count++;
425 }
426 }
427 fz_always(ctx)
428 fz_free(ctx, name);
429 fz_catch(ctx)
430 fz_rethrow(ctx);
431 }
432
433 static int read_zip_entry_header(fz_context *ctx, fz_zip_archive *zip, zip_entry *ent)
434 {
435 fz_stream *file = zip->super.file;
436 uint32_t sig;
437 int general, method, namelength, extralength;
438
439 fz_seek(ctx, file, ent->offset, 0);
440
441 sig = fz_read_uint32_le(ctx, file);
442 if (sig != ZIP_LOCAL_FILE_SIG)
443 fz_throw(ctx, FZ_ERROR_FORMAT, "wrong zip local file signature (0x%x)", sig);
444
445 (void) fz_read_uint16_le(ctx, file); /* version */
446 general = fz_read_uint16_le(ctx, file); /* general */
447 if (general & ZIP_ENCRYPTED_FLAG)
448 fz_throw(ctx, FZ_ERROR_FORMAT, "zip content is encrypted");
449
450 method = fz_read_uint16_le(ctx, file);
451 (void) fz_read_uint16_le(ctx, file); /* file time */
452 (void) fz_read_uint16_le(ctx, file); /* file date */
453 (void) fz_read_uint32_le(ctx, file); /* crc-32 */
454 (void) fz_read_uint32_le(ctx, file); /* csize */
455 (void) fz_read_uint32_le(ctx, file); /* usize */
456 namelength = fz_read_uint16_le(ctx, file);
457 extralength = fz_read_uint16_le(ctx, file);
458
459 fz_seek(ctx, file, namelength + extralength, 1);
460
461 return method;
462 }
463
464 static void ensure_zip_entries(fz_context *ctx, fz_zip_archive *zip)
465 {
466 fz_stream *file = zip->super.file;
467 unsigned char buf[512];
468 size_t size, back, maxback;
469 size_t i, n;
470
471 fz_seek(ctx, file, 0, SEEK_END);
472 size = fz_tell(ctx, file);
473
474 maxback = fz_minz(size, 0xFFFF + sizeof buf);
475 back = fz_minz(maxback, sizeof buf);
476
477 while (back <= maxback)
478 {
479 fz_seek(ctx, file, (int64_t)(size - back), 0);
480 n = fz_read(ctx, file, buf, sizeof buf);
481 if (n < 4)
482 break;
483 for (i = n - 4; i > 0; i--)
484 if (!memcmp(buf + i, "PK\5\6", 4))
485 {
486 read_zip_dir_imp(ctx, zip, size - back + i);
487 return;
488 }
489 back += sizeof buf - 4;
490 }
491
492 fz_throw(ctx, FZ_ERROR_FORMAT, "cannot find end of central directory");
493 }
494
495 static zip_entry *lookup_zip_entry(fz_context *ctx, fz_zip_archive *zip, const char *name)
496 {
497 int i;
498 if (name[0] == '/')
499 ++name;
500 for (i = 0; i < zip->count; i++)
501 if (!fz_strcasecmp(name, zip->entries[i].name))
502 return &zip->entries[i];
503 return NULL;
504 }
505
506 static fz_stream *open_zip_entry(fz_context *ctx, fz_archive *arch, const char *name)
507 {
508 fz_zip_archive *zip = (fz_zip_archive *) arch;
509 fz_stream *file = zip->super.file;
510 int method;
511 zip_entry *ent;
512
513 ent = lookup_zip_entry(ctx, zip, name);
514 if (!ent)
515 return NULL;
516
517 method = read_zip_entry_header(ctx, zip, ent);
518 if (method == 0)
519 return fz_open_null_filter(ctx, file, ent->usize, fz_tell(ctx, file));
520 if (method == 8)
521 return fz_open_flated(ctx, file, -15);
522 fz_throw(ctx, FZ_ERROR_FORMAT, "unknown zip method: %d", method);
523 }
524
525 static fz_buffer *read_zip_entry(fz_context *ctx, fz_archive *arch, const char *name)
526 {
527 fz_zip_archive *zip = (fz_zip_archive *) arch;
528 fz_stream *file = zip->super.file;
529 fz_buffer *ubuf;
530 unsigned char *cbuf = NULL;
531 int method;
532 z_stream z;
533 int code;
534 uint64_t len;
535 zip_entry *ent;
536
537 fz_var(cbuf);
538
539 ent = lookup_zip_entry(ctx, zip, name);
540 if (!ent)
541 return NULL;
542
543 method = read_zip_entry_header(ctx, zip, ent);
544 ubuf = fz_new_buffer(ctx, ent->usize + 1); /* +1 because many callers will add a terminating zero */
545
546 if (method == 0)
547 {
548 fz_try(ctx)
549 {
550 ubuf->len = fz_read(ctx, file, ubuf->data, ent->usize);
551 if (ubuf->len < (size_t)ent->usize)
552 fz_warn(ctx, "premature end of data in stored zip archive entry");
553 }
554 fz_catch(ctx)
555 {
556 fz_drop_buffer(ctx, ubuf);
557 fz_rethrow(ctx);
558 }
559 return ubuf;
560 }
561 else if (method == 8)
562 {
563 fz_try(ctx)
564 {
565 cbuf = fz_malloc(ctx, ent->csize);
566
567 z.zalloc = fz_zlib_alloc;
568 z.zfree = fz_zlib_free;
569 z.opaque = ctx;
570 z.next_out = ubuf->data;
571 z.avail_out = ent->usize;
572 z.next_in = cbuf;
573 z.avail_in = (uInt)fz_read(ctx, file, cbuf, ent->csize);
574 if (z.avail_in < ent->csize)
575 fz_warn(ctx, "premature end of compressed data for compressed archive entry");
576
577 code = inflateInit2(&z, -15);
578 if (code != Z_OK)
579 {
580 fz_throw(ctx, FZ_ERROR_LIBRARY, "zlib inflateInit2 error: %s", z.msg);
581 }
582 code = inflate(&z, Z_FINISH);
583 if (code != Z_STREAM_END)
584 {
585 inflateEnd(&z);
586 fz_throw(ctx, FZ_ERROR_LIBRARY, "zlib inflate error: %s", z.msg);
587 }
588 code = inflateEnd(&z);
589 if (code != Z_OK)
590 {
591 fz_throw(ctx, FZ_ERROR_LIBRARY, "zlib inflateEnd error: %s", z.msg);
592 }
593
594 len = ent->usize - z.avail_out;
595 if (len < ent->usize)
596 fz_warn(ctx, "premature end of data in compressed archive entry");
597 ubuf->len = len;
598 }
599 fz_always(ctx)
600 {
601 fz_free(ctx, cbuf);
602 }
603 fz_catch(ctx)
604 {
605 fz_drop_buffer(ctx, ubuf);
606 fz_rethrow(ctx);
607 }
608 return ubuf;
609 }
610
611 fz_drop_buffer(ctx, ubuf);
612 fz_throw(ctx, FZ_ERROR_FORMAT, "unknown zip method: %d", method);
613 }
614
615 static int has_zip_entry(fz_context *ctx, fz_archive *arch, const char *name)
616 {
617 fz_zip_archive *zip = (fz_zip_archive *) arch;
618 zip_entry *ent = lookup_zip_entry(ctx, zip, name);
619 return ent != NULL;
620 }
621
622 static const char *list_zip_entry(fz_context *ctx, fz_archive *arch, int idx)
623 {
624 fz_zip_archive *zip = (fz_zip_archive *) arch;
625 if (idx < 0 || idx >= zip->count)
626 return NULL;
627 return zip->entries[idx].name;
628 }
629
630 static int count_zip_entries(fz_context *ctx, fz_archive *arch)
631 {
632 fz_zip_archive *zip = (fz_zip_archive *) arch;
633 return zip->count;
634 }
635
636 int
637 fz_is_zip_archive(fz_context *ctx, fz_stream *file)
638 {
639 const unsigned char signature[4] = { 'P', 'K', 0x03, 0x04 };
640 unsigned char data[4];
641 size_t n;
642
643 if (file == NULL)
644 return 0;
645
646 fz_seek(ctx, file, 0, 0);
647 n = fz_read(ctx, file, data, nelem(data));
648 if (n != nelem(signature))
649 return 0;
650 if (memcmp(data, signature, nelem(signature)))
651 return 0;
652
653 return 1;
654 }
655
656 fz_archive *
657 fz_open_zip_archive_with_stream(fz_context *ctx, fz_stream *file)
658 {
659 fz_zip_archive *zip;
660
661 if (!fz_is_zip_archive(ctx, file))
662 fz_throw(ctx, FZ_ERROR_FORMAT, "cannot recognize zip archive");
663
664 zip = fz_new_derived_archive(ctx, file, fz_zip_archive);
665 zip->super.format = "zip";
666 zip->super.count_entries = count_zip_entries;
667 zip->super.list_entry = list_zip_entry;
668 zip->super.has_entry = has_zip_entry;
669 zip->super.read_entry = read_zip_entry;
670 zip->super.open_entry = open_zip_entry;
671 zip->super.drop_archive = drop_zip_archive;
672
673 fz_try(ctx)
674 {
675 ensure_zip_entries(ctx, zip);
676 }
677 fz_catch(ctx)
678 {
679 fz_drop_archive(ctx, &zip->super);
680 fz_rethrow(ctx);
681 }
682
683 return &zip->super;
684 }
685
686 fz_archive *
687 fz_open_zip_archive(fz_context *ctx, const char *filename)
688 {
689 fz_archive *zip = NULL;
690 fz_stream *file;
691
692 file = fz_open_file(ctx, filename);
693
694 fz_var(zip);
695
696 fz_try(ctx)
697 zip = fz_open_zip_archive_with_stream(ctx, file);
698 fz_always(ctx)
699 fz_drop_stream(ctx, file);
700 fz_catch(ctx)
701 fz_rethrow(ctx);
702
703 return zip;
704 }