#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <zzip.h>
#include <zzip-file.h>
#include "strc.h"
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <ctype.h>
#include <zzipformat.h>
#if 0
# if defined ZZIP_HAVE_IO_H
# include <io.h>
# else
# define tell(fd) lseek(fd,0,SEEK_CUR)
# endif
#else
#define tells(fd) seeks(fd,0,SEEK_CUR)
#endif
int
zzip_file_close(ZZIP_FILE * fp)
{
ZZIP_DIR * dir = fp->dir;
if (fp->method)
inflateEnd(&fp->d_stream);
if (fp->buf32k)
{
if (dir->cache.buf32k == NULL) dir->cache.buf32k = fp->buf32k;
else free(fp->buf32k);
}
if (dir->currentfp == fp)
dir->currentfp = NULL;
dir->refcount--;
memset(fp, 0, sizeof(*fp));
if (dir->cache.fp == NULL) dir->cache.fp = fp;
else free(fp);
if (! dir->refcount) return zzip_dir_close(dir); else return 0;
}
static int
zzip_file_saveoffset(ZZIP_FILE * fp)
{
if (fp)
{
int fd = fp->dir->fd;
zzip_off_t off = fp->io->seeks(fd, 0, SEEK_CUR);
if (off < 0)
return -1;
fp->offset = off;
}
return 0;
}
# if !defined strcasecmp && !defined ZZIP_HAVE_STRCASECMP
# define ZZIP_CHECK_BACKSLASH_DIRSEPARATOR 1
# else
# ifndef ZZIP_CHECK_BACKSLASH_DIRSEPARATOR
# define ZZIP_CHECK_BACKSLASH_DIRSEPARATOR 0
# endif
# endif
#if ! ZZIP_CHECK_BACKSLASH_DIRSEPARATOR+0
#define dirsep_strrchr(N,C) strrchr(N,C)
#define dirsep_casecmp strcasecmp
#else
#define dirsep_strrchr(N,C) _dirsep_strrchr(N)
#define dirsep_casecmp _dirsep_casecmp
static zzip_char_t*
_dirsep_strrchr (zzip_char_t* name)
{
char* n = strrchr (name, '/');
char* m = strrchr (name, '\\');
if (m && n && m > n) n = m;
return n;
}
static int
_dirsep_casecmp (zzip_char_t* s1, zzip_char_t* s2)
{
static const char mapping[] = "@abcdefghijklmnopqrstuvwxyz[/]^_";
int c1, c2;
while (*s1 && *s2)
{
c1 = (int)(unsigned char) *s1;
c2 = (int)(unsigned char) *s2;
if ((c1&0xE0) == 0x40) c1 = mapping[c1&0x1f];
if ((c1&0xE0) == 0x40) c2 = mapping[c2&0x1f];
if (c1 != c2)
return (c1 - c2);
s1++; s2++;
}
return (((int)(unsigned char) *s1) - ((int)(unsigned char) *s2));
}
#endif
static int zzip_inflate_init(ZZIP_FILE *, struct zzip_dir_hdr *);
ZZIP_FILE *
zzip_file_open(ZZIP_DIR * dir, zzip_char_t* name, int o_mode)
{
zzip_error_t err = 0;
struct zzip_file * fp = 0;
struct zzip_dir_hdr * hdr = dir->hdr0;
int (*cmp)(zzip_char_t*, zzip_char_t*);
cmp = (o_mode & ZZIP_CASELESS)? dirsep_casecmp: strcmp;
if (! dir || !dir->fd || dir->fd == -1 ) return 0;
if (o_mode & ZZIP_NOPATHS)
{
register zzip_char_t* n = dirsep_strrchr(name, '/');
if (n) name = n + 1;
}
if (hdr)
while (1)
{
register zzip_char_t* hdr_name = hdr->d_name;
if (o_mode & ZZIP_NOPATHS)
{
register zzip_char_t* n = dirsep_strrchr(hdr_name, '/');
if (n) hdr_name = n + 1;
}
if (!cmp(hdr_name, name))
{
switch (hdr->d_compr)
{
case 0:
case 8:
break;
default:
{ err = ZZIP_UNSUPP_COMPR; goto error; }
}
if (dir->cache.fp)
{
fp = dir->cache.fp; dir->cache.fp = NULL;
}else
{
if (! (fp = (ZZIP_FILE *)calloc(1, sizeof(*fp))))
{ err = ZZIP_OUTOFMEM; goto error; }
}
fp->dir = dir;
fp->io = dir->io;
dir->refcount++;
if (dir->cache.buf32k)
{ fp->buf32k = dir->cache.buf32k; dir->cache.buf32k = NULL; }
else
{
if (! (fp->buf32k = (char *)malloc(ZZIP_32K)))
{ err = ZZIP_OUTOFMEM; goto error; }
}
if (zzip_file_saveoffset(dir->currentfp) < 0)
{ err = ZZIP_DIR_SEEK; goto error; }
fp->offset = hdr->d_off;
dir->currentfp = fp;
if (dir->io->seeks(dir->fd, hdr->d_off, SEEK_SET) < 0)
{ err = ZZIP_DIR_SEEK; goto error; }
{
zzip_ssize_t dataoff;
struct zzip_file_header * p = (void*) fp->buf32k;
dataoff = dir->io->read(dir->fd, (void*)p, sizeof(*p));
if (dataoff < (zzip_ssize_t)sizeof(*p))
{ err = ZZIP_DIR_READ; goto error; }
if (! ZZIP_FILE_HEADER_CHECKMAGIC(p))
{ err = ZZIP_CORRUPTED; goto error; }
dataoff = ZZIP_GET16(p->z_namlen) + ZZIP_GET16(p->z_extras);
if (dir->io->seeks(dir->fd, dataoff, SEEK_CUR) < 0)
{ err = ZZIP_DIR_SEEK; goto error; }
fp->dataoffset = dir->io->tells(dir->fd);
fp->usize = hdr->d_usize;
fp->csize = hdr->d_csize;
}
err = zzip_inflate_init (fp, hdr);
if (err) { goto error; }
return fp;
}else
{
if (hdr->d_reclen == 0)
break;
hdr = (struct zzip_dir_hdr *)((char *)hdr + hdr->d_reclen);
}
}
dir->errcode = ZZIP_ENOENT; zzip_errno(ZZIP_ENOENT);
return NULL;
error:
if (fp) zzip_file_close(fp);
dir->errcode = err; zzip_errno(err);
return NULL;
}
static int
zzip_inflate_init(ZZIP_FILE * fp, struct zzip_dir_hdr* hdr)
{
int err;
fp->method = hdr->d_compr;
fp->restlen = hdr->d_usize;
if (fp->method)
{
memset(&fp->d_stream, 0, sizeof(fp->d_stream));
err = inflateInit2(&fp->d_stream, -MAX_WBITS);
if (err != Z_OK) { goto error; }
fp->crestlen = hdr->d_csize;
}
return 0;
error:
if (fp) zzip_file_close(fp);
return err;
}
int
zzip_fclose(ZZIP_FILE * fp)
{
if (! fp) return 0;
if (! fp->dir)
{ int r = fp->io->close(fp->fd); free(fp); return r; }
else return zzip_file_close(fp);
}
int
zzip_close(ZZIP_FILE* fp)
{
return zzip_fclose (fp);
}
zzip_ssize_t
zzip_file_read(ZZIP_FILE * fp, char * buf, zzip_size_t len)
{
ZZIP_DIR * dir;
zzip_size_t l;
zzip_ssize_t rv;
if (! fp || ! fp->dir) return 0;
dir = fp->dir;
l = fp->restlen > len ? len : fp->restlen;
if (fp->restlen == 0)
return 0;
if (dir->currentfp != fp)
{
if (zzip_file_saveoffset(dir->currentfp) < 0
|| fp->io->seeks(dir->fd, fp->offset, SEEK_SET) < 0)
{ dir->errcode = ZZIP_DIR_SEEK; return -1; }
else
{ dir->currentfp = fp; }
}
if (fp->method)
{
fp->d_stream.avail_out = l;
fp->d_stream.next_out = (unsigned char *)buf;
do {
int err;
zzip_size_t startlen;
if (fp->crestlen > 0 && fp->d_stream.avail_in == 0)
{
zzip_size_t cl = ( fp->crestlen < ZZIP_32K ?
fp->crestlen : ZZIP_32K );
zzip_ssize_t i = fp->io->read(dir->fd, fp->buf32k, cl);
if (i <= 0)
{
dir->errcode = ZZIP_DIR_READ;
return -1;
}
fp->crestlen -= i;
fp->d_stream.avail_in = i;
fp->d_stream.next_in = (unsigned char *)fp->buf32k;
}
startlen = fp->d_stream.total_out;
err = inflate(&fp->d_stream, Z_NO_FLUSH);
if (err == Z_STREAM_END)
{ fp->restlen = 0; }
else
if (err == Z_OK)
{ fp->restlen -= (fp->d_stream.total_out - startlen); }
else
{ dir->errcode = err; return -1; }
} while (fp->restlen && fp->d_stream.avail_out);
return l - fp->d_stream.avail_out;
}else
{
rv = fp->io->read(dir->fd, buf, l);
if (rv > 0)
{ fp->restlen-= rv; }
else
if (rv < 0)
{ dir->errcode = ZZIP_DIR_READ; }
return rv;
}
}
zzip_ssize_t
zzip_read(ZZIP_FILE * fp, char * buf, zzip_size_t len)
{
if (! fp) return 0;
if (! fp->dir)
{ return fp->io->read(fp->fd, buf, len); }
else
{ register zzip_ssize_t v;
v = zzip_file_read(fp, buf, len);
if (v == -1) { errno = zzip_errno(fp->dir->errcode); }
return v;
}
}
zzip_size_t
zzip_fread(void *ptr, zzip_size_t size, zzip_size_t nmemb, ZZIP_FILE *file)
{
if (! size) size=1;
return zzip_read (file, ptr, size*nmemb)/size;
}
#define ZZIP_WRONLY O_WRONLY
#define ZZIP_EXCL O_EXCL
#if defined O_SYNC
#define ZZIP_SYNC O_SYNC
#else
#define ZZIP_SYNC 0
#endif
#if defined O_NONBLOCK
#define ZZIP_NONBLOCK O_NONBLOCK
#elif defined O_NDELAY
#define ZZIP_NOCTTY O_NDELAY
#else
#define ZZIP_NOCTTY 0
#endif
ZZIP_FILE*
zzip_fopen(zzip_char_t* filename, zzip_char_t* mode)
{
return zzip_freopen (filename, mode, 0);
}
ZZIP_FILE*
zzip_freopen(zzip_char_t* filename, zzip_char_t* mode, ZZIP_FILE* stream)
{
int o_flags = 0;
int o_modes = 0664;
if (!mode) mode = "rb";
# ifndef O_BINARY
# define O_BINARY 0
# endif
# ifndef O_NOCTTY
# define O_NOCTTY 0
# endif
# ifndef O_SYNC
# define O_SYNC 0
# endif
# ifndef O_NONBLOCK
# define O_NONBLOCK 0
# endif
for(; *mode; mode++)
{
switch (*mode)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
continue;
case 'r': o_flags |= mode[1] == '+' ? O_RDWR : O_RDONLY; break;
case 'w': o_flags |= mode[1] == '+' ? O_RDWR : O_WRONLY;
o_flags |= O_TRUNC; break;
case 'b': o_flags |= O_BINARY; break;
case 'f': o_flags |= O_NOCTTY; break;
case 'i': o_modes |= ZZIP_CASELESS; break;
case '*': o_modes |= ZZIP_NOPATHS; break;
case 'x': o_flags |= O_EXCL; break;
case 's': o_flags |= O_SYNC; break;
case 'n': o_flags |= O_NONBLOCK; break;
case 'o': o_modes &=~ 07;
o_modes |= ((mode[1] - '0'))&07; continue;
case 'g': o_modes &=~ 070;
o_modes |= ((mode[1] - '0')<<3)&070; continue;
case 'u': o_modes &=~ 0700;
o_modes |= ((mode[1] - '0')<<6)&0700; continue;
case 'q': o_modes |= ZZIP_FACTORY; break;
case 'z':
continue;
}
}
{
ZZIP_FILE* fp =
zzip_open_shared_io (stream, filename, o_flags, o_modes, 0, 0);
if (! o_modes&ZZIP_FACTORY && stream)
zzip_file_close (stream);
return fp;
}
}
ZZIP_FILE*
zzip_open(zzip_char_t* filename, int o_flags)
{
int o_modes = 0664;
if (o_flags & ZZIP_CASEINSENSITIVE)
{ o_flags ^= ZZIP_CASEINSENSITIVE; o_modes |= ZZIP_CASELESS; }
if (o_flags & ZZIP_IGNOREPATH)
{ o_flags ^= ZZIP_IGNOREPATH; o_modes |= ZZIP_NOPATHS; }
return zzip_open_ext_io(filename, o_flags, o_modes, 0, 0);
}
#if ZZIP_SIZEOF_INT+0 == 2
#undef ZZIP_ONLYZIP
#endif
ZZIP_FILE*
zzip_open_ext_io(zzip_char_t* filename, int o_flags, int o_modes,
zzip_strings_t* ext, zzip_plugin_io_t io)
{
return zzip_open_shared_io (0, filename, o_flags, o_modes, ext, io);
}
ZZIP_FILE*
zzip_open_shared_io (ZZIP_FILE* stream,
zzip_char_t* filename, int o_flags, int o_modes,
zzip_strings_t* ext, zzip_plugin_io_t io)
{
if (stream && stream->dir)
{
if (! ext) ext = stream->dir->fileext;
if (! io) io = stream->dir->io;
}
if (! io) io = zzip_get_default_io ();
if (o_modes & (ZZIP_PREFERZIP|ZZIP_ONLYZIP)) goto try_zzip;
try_real:
{
zzip_plugin_io_t os = (o_modes & ZZIP_ALLOWREAL)
? zzip_get_default_io () : io;
int fd = os->open(filename, o_flags);
if (fd != -1)
{
ZZIP_FILE* fp = calloc (1, sizeof(ZZIP_FILE));
if (!fp) { os->close(fd); return 0; }
fp->fd = fd;
fp->io = os;
return fp;
}
if (o_modes & ZZIP_PREFERZIP) return 0;
}
try_zzip:
if (o_flags & (O_CREAT|O_WRONLY)) { errno = EINVAL; return 0; }
if (o_flags & (O_RDWR)) { o_flags ^= O_RDWR; o_flags |= O_RDONLY; }
{ char basename[PATH_MAX];
char* p;
strcpy (basename, filename);
if (stream && stream->dir && stream->dir->realname)
{
zzip_size_t len = strlen (stream->dir->realname);
if (! memcmp (filename, stream->dir->realname, len) &&
filename[len] == '/' && filename[len+1])
{
ZZIP_FILE* fp =
zzip_file_open (stream->dir, filename+len+1, o_modes);
if (! fp) { errno = zzip_errno (stream->dir->errcode); }
return fp;
}
}
while ((p = strrchr (basename, '/')))
{
zzip_error_t e = 0;
ZZIP_DIR* dir;
ZZIP_FILE* fp;
int fd;
*p = '\0';
fd = __zzip_try_open (basename, o_flags|O_RDONLY|O_BINARY, ext, io);
if (fd == -1) { continue; }
dir = zzip_dir_fdopen_ext_io(fd, &e, ext, io);
if (e) { errno = zzip_errno(e); io->close(fd); return 0; }
fp = zzip_file_open(dir, filename + (p - basename) +1, o_modes);
if (! fp) { errno = zzip_errno(dir->errcode); }
else { if (! dir->realname) dir->realname = strdup (basename); }
zzip_dir_close(dir);
return fp;
}
if (o_modes & ZZIP_PREFERZIP) goto try_real;
errno = ENOENT; return 0;
}
}
#if defined ZZIP_LARGEFILE_RENAME && defined EOVERFLOW && defined PIC
#undef zzip_open_shared_io
#undef zzip_open_ext_io
#undef zzip_opendir_ext_io
_zzip_export
ZZIP_FILE * zzip_open_shared_io(ZZIP_FILE* stream,
zzip_char_t* name, int o_flags, int o_modes,
zzip_strings_t* ext, zzip_plugin_io_t io)
{
if (! io) return zzip_open_shared_io64 (stream, name, o_flags, o_modes,
ext, io);
errno = EOVERFLOW; return NULL;
}
_zzip_export
ZZIP_FILE * zzip_open_ext_io(zzip_char_t* name, int o_flags, int o_modes,
zzip_strings_t* ext, zzip_plugin_io_t io)
{
if (! io) return zzip_open_ext_io64 (name, o_flags, o_modes, ext, io);
errno = EOVERFLOW; return NULL;
}
_zzip_export
ZZIP_DIR * zzip_opendir_ext_io(zzip_char_t* name, int o_modes,
zzip_strings_t* ext, zzip_plugin_io_t io)
{
if (! io) return zzip_opendir_ext_io64 (name, o_modes, ext, io);
errno = EOVERFLOW; return NULL;
}
#endif
int
zzip_rewind(ZZIP_FILE *fp)
{
ZZIP_DIR *dir;
int err;
if (! fp)
return -1;
if (! fp->dir)
{
fp->io->seeks(fp->fd,0,SEEK_SET);
return 0;
}
dir = fp->dir;
if (dir->currentfp != fp)
{
if (zzip_file_saveoffset(dir->currentfp) < 0)
{ dir->errcode = ZZIP_DIR_SEEK; return -1; }
else
{ dir->currentfp = fp; }
}
if (fp->io->seeks(dir->fd, fp->dataoffset, SEEK_SET) < 0)
return -1;
fp->restlen = fp->usize;
fp->offset = fp->dataoffset;
if (fp->method)
{
memset(&fp->d_stream, 0, sizeof fp->d_stream);
err = inflateInit2(&fp->d_stream, -MAX_WBITS);
if (err != Z_OK) { goto error; }
fp->crestlen = fp->csize;
}
return 0;
error:
if (fp) zzip_file_close(fp);
return err;
}
zzip_off_t
zzip_seek(ZZIP_FILE * fp, zzip_off_t offset, int whence)
{
zzip_off_t cur_pos, rel_ofs, read_size, ofs;
ZZIP_DIR *dir;
if (! fp)
return -1;
if (! fp->dir)
{
return fp->io->seeks(fp->fd, offset, whence);
}
cur_pos = zzip_tell(fp);
switch (whence)
{
case SEEK_SET:
rel_ofs = offset - cur_pos;
break;
case SEEK_CUR:
rel_ofs = offset;
break;
case SEEK_END:
rel_ofs = fp->usize + offset - cur_pos;
break;
default:
return -1;
}
if (rel_ofs == 0)
return cur_pos;
if (rel_ofs < 0)
{
if (zzip_rewind(fp) == -1)
return -1;
read_size = cur_pos + rel_ofs;
cur_pos = 0;
}else
{
read_size = rel_ofs;
}
if (read_size < 0)
return -1;
if (read_size + cur_pos > (zzip_off_t)fp->usize)
return -1;
if (read_size == 0)
return cur_pos;
dir = fp->dir;
if (dir->currentfp != fp)
{
if (zzip_file_saveoffset(dir->currentfp) < 0
|| dir->currentfp->io->seeks(dir->fd, fp->offset, SEEK_SET) < 0)
{ dir->errcode = ZZIP_DIR_SEEK; return -1; }
else
{ dir->currentfp = fp; }
}
if (fp->method == 0)
{
ofs = fp->io->tells(dir->fd);
ofs = fp->io->seeks(dir->fd,read_size,SEEK_CUR);
if (ofs > 0)
{
ofs -= fp->dataoffset;
fp->restlen = fp->usize - ofs;
}
return ofs;
}else
{
char *buf;
buf = (char *)malloc(ZZIP_32K);
if (! buf) return -1;
while (read_size > 0)
{
zzip_off_t size = ZZIP_32K;
if (read_size < size) size = read_size;
size = zzip_file_read(fp, buf, (zzip_size_t)size);
if (size <= 0) { free(buf); return -1; }
read_size -= size;
}
free (buf);
}
return zzip_tell(fp);
}
zzip_off_t
zzip_tell(ZZIP_FILE * fp)
{
if (! fp)
return -1;
if (! fp->dir)
return fp->io->tells(fp->fd);
return (fp->usize - fp->restlen);
}