diff options
author | Sean Finney <seanius@debian.org> | 2009-04-10 14:09:48 +0200 |
---|---|---|
committer | Sean Finney <seanius@debian.org> | 2009-04-10 14:09:48 +0200 |
commit | cd0b49c72aee33b3e44a9c589fcd93b9e1c7a64f (patch) | |
tree | 1315c623bb7d9dfa8d366fa9cd2c6834ceeb5da5 /ext/zip/lib/zip_open.c | |
parent | 9ea47aab740772adf0c69d8c94b208a464e599ea (diff) | |
download | php-cd0b49c72aee33b3e44a9c589fcd93b9e1c7a64f.tar.gz |
Imported Upstream version 5.2.9.dfsg.1upstream/5.2.9.dfsg.1
Diffstat (limited to 'ext/zip/lib/zip_open.c')
-rw-r--r-- | ext/zip/lib/zip_open.c | 377 |
1 files changed, 235 insertions, 142 deletions
diff --git a/ext/zip/lib/zip_open.c b/ext/zip/lib/zip_open.c index 711dcb1ca..0880eef2c 100644 --- a/ext/zip/lib/zip_open.c +++ b/ext/zip/lib/zip_open.c @@ -1,11 +1,9 @@ /* - $NiH: zip_open.c,v 1.38 2006/05/04 00:01:26 dillo Exp $ - zip_open.c -- open zip archive - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. - The authors can be contacted at <nih@giga.or.at> + The authors can be contacted at <libzip@nih.at> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -37,18 +35,19 @@ #include <sys/stat.h> #include <errno.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#ifndef _MSC_VER -#include <unistd.h> -#endif -#include <sys/types.h> -#include "zip.h" + #include "zipint.h" static void set_error(int *, struct zip_error *, int); +static struct zip *_zip_allocate_new(const char *, int *); static int _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *); +static void _zip_check_torrentzip(struct zip *); +static struct zip_cdir *_zip_find_central_dir(FILE *, int, int *, off_t); +static int _zip_file_exists(const char *, int, int *); static int _zip_headercomp(struct zip_dirent *, int, struct zip_dirent *, int); static unsigned char *_zip_memmem(const unsigned char *, int, @@ -58,154 +57,58 @@ static struct zip_cdir *_zip_readcdir(FILE *, unsigned char *, unsigned char *, -PHPZIPAPI struct zip * +ZIP_EXTERN(struct zip *) zip_open(const char *fn, int flags, int *zep) { FILE *fp; - unsigned char *buf, *match; - int a, i, buflen, best; struct zip *za; - struct zip_cdir *cdir, *cdirnew; - long len; - struct stat st; - struct zip_error error, err2; - - if (fn == NULL) { - set_error(zep, NULL, ZIP_ER_INVAL); + struct zip_cdir *cdir; + int i; + off_t len; + + switch (_zip_file_exists(fn, flags, zep)) { + case -1: + if (!(flags & ZIP_OVERWRITE)) { return NULL; + } + + case 0: + return _zip_allocate_new(fn, zep); + default: + break; } - if (flags & ZIP_OVERWRITE || stat(fn, &st) != 0) { - if ((flags & ZIP_CREATE) || (flags & ZIP_OVERWRITE)) { - if ((za=_zip_new(&error)) == NULL) { - set_error(zep, &error, 0); - return NULL; - } - - za->zn = strdup(fn); - if (!za->zn) { - _zip_free(za); - set_error(zep, NULL, ZIP_ER_MEMORY); - return NULL; - } - return za; - } - else { - set_error(zep, NULL, ZIP_ER_OPEN); - return NULL; - } - } - else if ((flags & ZIP_EXCL)) { - set_error(zep, NULL, ZIP_ER_EXISTS); + if ((fp=fopen(fn, "rb")) == NULL) { + set_error(zep, NULL, ZIP_ER_OPEN); return NULL; } - /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, - just like open() */ - if ((fp=fopen(fn, "rb")) == NULL) { - set_error(zep, NULL, ZIP_ER_OPEN); - return NULL; - } - -#ifdef PHP_WIN32 - _setmode(_fileno(fp), _O_BINARY ); -#endif + fseeko(fp, 0, SEEK_END); + len = ftello(fp); - clearerr(fp); - fseek(fp, 0, SEEK_END); - len = ftell(fp); - i = fseek(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END); - if (i == -1 && errno != EFBIG) { - /* seek before start of file on my machine */ - set_error(zep, NULL, ZIP_ER_SEEK); - fclose(fp); - return NULL; - } - - /* 64k is too much for stack */ - if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) { - set_error(zep, NULL, ZIP_ER_MEMORY); - fclose(fp); - return NULL; + /* treat empty files as empty archives */ + if (len == 0) { + if ((za=_zip_allocate_new(fn, zep)) == NULL) + fclose(fp); + else + za->zp = fp; + return za; } - clearerr(fp); - buflen = fread(buf, 1, CDBUFSIZE, fp); - - if (ferror(fp)) { - set_error(zep, NULL, ZIP_ER_READ); - free(buf); + cdir = _zip_find_central_dir(fp, flags, zep, len); + if (cdir == NULL) { fclose(fp); return NULL; } - - best = -2; - cdir = NULL; - match = buf; - while ((match=_zip_memmem(match, buflen-(match-buf)-18, - (const unsigned char *)EOCD_MAGIC, 4))!=NULL) { - /* found match -- check, if good */ - /* to avoid finding the same match all over again */ - match++; - if ((cdirnew=_zip_readcdir(fp, buf, match-1, buflen, flags, - &err2)) == NULL) { - if (best == -2) { - set_error(zep, &err2, 0); - best = -1; - } - continue; - } - if (cdir) { - if (best <= 0) - best = _zip_checkcons(fp, cdir, &err2); - a = _zip_checkcons(fp, cdirnew, &err2); - if (best < a) { - _zip_cdir_free(cdir); - cdir = cdirnew; - best = a; - } - else - _zip_cdir_free(cdirnew); - } - else { - cdir = cdirnew; - if (flags & ZIP_CHECKCONS) - best = _zip_checkcons(fp, cdir, &err2); - else - best = 0; - } - cdirnew = NULL; - } - - free(buf); - - if (best < 0) { - /* no consistent eocd found */ - if (best == -2) { - /* no eocd found at all */ - set_error(zep, NULL, ZIP_ER_NOZIP); - } - _zip_cdir_free(cdir); - fclose(fp); - return NULL; - } - - if ((za=_zip_new(&error)) == NULL) { - set_error(zep, &error, 0); + if ((za=_zip_allocate_new(fn, zep)) == NULL) { _zip_cdir_free(cdir); fclose(fp); return NULL; } - za->zp = fp; za->cdir = cdir; - - if ((za->zn=strdup(fn)) == NULL) { - set_error(zep, NULL, ZIP_ER_MEMORY); - _zip_free(za); - return NULL; - } + za->zp = fp; if ((za->entry=(struct zip_entry *)malloc(sizeof(*(za->entry)) * cdir->nentry)) == NULL) { @@ -216,6 +119,9 @@ zip_open(const char *fn, int flags, int *zep) for (i=0; i<cdir->nentry; i++) _zip_entry_new(za); + _zip_check_torrentzip(za); + za->ch_flags = za->flags; + return za; } @@ -295,13 +201,14 @@ _zip_readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen, return NULL; } - if (cd->comment_len) + if (cd->comment_len) { if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN, cd->comment_len, error)) == NULL) { free(cd); return NULL; } + } cdp = eocd; if (cd->size < (unsigned int)(eocd-buf)) { @@ -313,8 +220,10 @@ _zip_readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen, /* go to start of cdir and read it entry by entry */ bufp = NULL; clearerr(fp); - fseek(fp, -(cd->size+cd->comment_len+EOCDLEN), SEEK_END); - if (ferror(fp) || ((unsigned int)ftell(fp) != cd->offset)) { + fseeko(fp, cd->offset, SEEK_SET); + /* possible consistency check: cd->offset = + len-(cd->size+cd->comment_len+EOCDLEN) ? */ + if (ferror(fp) || ((unsigned long)ftello(fp) != cd->offset)) { /* seek error or offset of cdir wrong */ if (ferror(fp)) _zip_error_set(error, ZIP_ER_SEEK, errno); @@ -376,7 +285,7 @@ _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) return -1; } - if (fseek(fp, cd->entry[i].offset, SEEK_SET) != 0) { + if (fseeko(fp, cd->entry[i].offset, SEEK_SET) != 0) { _zip_error_set(error, ZIP_ER_SEEK, 0); return -1; } @@ -385,7 +294,7 @@ _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) return -1; if (_zip_headercomp(cd->entry+i, 0, &temp, 1) != 0) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); + _zip_error_set(error, ZIP_ER_INCONS, 0); _zip_dirent_finalize(&temp); return -1; } @@ -397,6 +306,41 @@ _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) +/* _zip_check_torrentzip: + check wether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */ + +static void +_zip_check_torrentzip(struct zip *za) +{ + uLong crc_got, crc_should; + char buf[8+1]; + char *end; + + if (za->zp == NULL || za->cdir == NULL) + return; + + if (za->cdir->comment_len != TORRENT_SIG_LEN+8 + || strncmp(za->cdir->comment, TORRENT_SIG, TORRENT_SIG_LEN) != 0) + return; + + memcpy(buf, za->cdir->comment+TORRENT_SIG_LEN, 8); + buf[8] = '\0'; + errno = 0; + crc_should = strtoul(buf, &end, 16); + if ((crc_should == UINT_MAX && errno != 0) || (end && *end)) + return; + + if (_zip_filerange_crc(za->zp, za->cdir->offset, za->cdir->size, + &crc_got, NULL) < 0) + return; + + if (crc_got == crc_should) + za->flags |= ZIP_AFL_TORRENT; +} + + + + /* _zip_headercomp: compares two headers h1 and h2; if they are local headers, set local1p or local2p respectively to 1, else 0. Return 0 if they @@ -414,14 +358,32 @@ _zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2, #endif || (h1->comp_method != h2->comp_method) || (h1->last_mod != h2->last_mod) - || (h1->crc != h2->crc) - || (h1->comp_size != h2->comp_size) - || (h1->uncomp_size != h2->uncomp_size) || (h1->filename_len != h2->filename_len) || !h1->filename || !h2->filename || strcmp(h1->filename, h2->filename)) return -1; + /* check that CRC and sizes are zero if data descriptor is used */ + if ((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local1p + && (h1->crc != 0 + || h1->comp_size != 0 + || h1->uncomp_size != 0)) + return -1; + if ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local2p + && (h2->crc != 0 + || h2->comp_size != 0 + || h2->uncomp_size != 0)) + return -1; + + /* check that CRC and sizes are equal if no data descriptor is used */ + if (((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local1p == 0) + && ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local2p == 0)) { + if ((h1->crc != h2->crc) + || (h1->comp_size != h2->comp_size) + || (h1->uncomp_size != h2->uncomp_size)) + return -1; + } + if ((local1p == local2p) && ((h1->extrafield_len != h2->extrafield_len) || (h1->extrafield_len && h2->extrafield @@ -448,6 +410,137 @@ _zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2, +static struct zip * +_zip_allocate_new(const char *fn, int *zep) +{ + struct zip *za; + struct zip_error error; + + if ((za=_zip_new(&error)) == NULL) { + set_error(zep, &error, 0); + return NULL; + } + + za->zn = strdup(fn); + if (!za->zn) { + _zip_free(za); + set_error(zep, NULL, ZIP_ER_MEMORY); + return NULL; + } + return za; +} + + + +static int +_zip_file_exists(const char *fn, int flags, int *zep) +{ + struct stat st; + + if (fn == NULL) { + set_error(zep, NULL, ZIP_ER_INVAL); + return -1; + } + + if (stat(fn, &st) != 0) { + if (flags & ZIP_CREATE || flags & ZIP_OVERWRITE) + return 0; + else { + set_error(zep, NULL, ZIP_ER_OPEN); + return -1; + } + } + else if ((flags & ZIP_EXCL)) { + set_error(zep, NULL, ZIP_ER_EXISTS); + return -1; + } + /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, + just like open() */ + + return 1; +} + + + +static struct zip_cdir * +_zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len) +{ + struct zip_cdir *cdir, *cdirnew; + unsigned char *buf, *match; + int a, best, buflen, i; + struct zip_error zerr; + + i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END); + if (i == -1 && errno != EFBIG) { + /* seek before start of file on my machine */ + set_error(zep, NULL, ZIP_ER_SEEK); + return NULL; + } + + /* 64k is too much for stack */ + if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) { + set_error(zep, NULL, ZIP_ER_MEMORY); + return NULL; + } + + clearerr(fp); + buflen = fread(buf, 1, CDBUFSIZE, fp); + + if (ferror(fp)) { + set_error(zep, NULL, ZIP_ER_READ); + free(buf); + return NULL; + } + + best = -1; + cdir = NULL; + match = buf; + _zip_error_set(&zerr, ZIP_ER_NOZIP, 0); + + while ((match=_zip_memmem(match, buflen-(match-buf)-18, + (const unsigned char *)EOCD_MAGIC, 4))!=NULL) { + /* found match -- check, if good */ + /* to avoid finding the same match all over again */ + match++; + if ((cdirnew=_zip_readcdir(fp, buf, match-1, buflen, flags, + &zerr)) == NULL) + continue; + + if (cdir) { + if (best <= 0) + best = _zip_checkcons(fp, cdir, &zerr); + a = _zip_checkcons(fp, cdirnew, &zerr); + if (best < a) { + _zip_cdir_free(cdir); + cdir = cdirnew; + best = a; + } + else + _zip_cdir_free(cdirnew); + } + else { + cdir = cdirnew; + if (flags & ZIP_CHECKCONS) + best = _zip_checkcons(fp, cdir, &zerr); + else + best = 0; + } + cdirnew = NULL; + } + + free(buf); + + if (best < 0) { + set_error(zep, &zerr, 0); + _zip_cdir_free(cdir); + return NULL; + } + + return cdir; +} + + + static unsigned char * _zip_memmem(const unsigned char *big, int biglen, const unsigned char *little, int littlelen) |