Checkpoint commit of stacked sources to implement encryption/compression.

Used for zip_fread.
Not yet used for writing (zip_close).
Traditional PKWARE encryption untested.

--HG--
branch : HEAD
diff --git a/lib/Makefile.am b/lib/Makefile.am
index b6d7b13..68dbaf8 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -32,11 +32,15 @@
 	zip_file_strerror.c \
 	zip_filerange_crc.c \
 	zip_fopen.c \
+	zip_fopen_encrypted.c \
 	zip_fopen_index.c \
+	zip_fopen_index_encrypted.c \
 	zip_fread.c \
 	zip_free.c \
 	zip_get_archive_comment.c \
 	zip_get_archive_flag.c \
+	zip_get_compression_implementation.c \
+	zip_get_encryption_implementation.c \
 	zip_get_file_comment.c \
 	zip_get_num_files.c \
 	zip_get_name.c \
@@ -50,10 +54,13 @@
 	zip_set_archive_flag.c \
 	zip_set_file_comment.c \
 	zip_source_buffer.c \
+	zip_source_call.c \
+	zip_source_deflate.c \
 	zip_source_file.c \
 	zip_source_filep.c \
 	zip_source_free.c \
 	zip_source_function.c \
+	zip_source_pkware.c \
 	zip_source_zip.c \
 	zip_set_name.c \
 	zip_stat.c \
diff --git a/lib/zip.h b/lib/zip.h
index 03b5640..66886e1 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -76,6 +76,12 @@
 #define ZIP_AFL_TORRENT		1 /* torrent zipped */
 #define ZIP_AFL_RDONLY		2 /* read only -- cannot be cleared */
 
+
+/* flags for compression and encryption sources */
+
+#define ZIP_CODEC_ENCODE	1 /* compress/encrypt */
+
+
 /* libzip error codes */
 
 #define ZIP_ER_OK             0  /* N No error */
@@ -105,7 +111,7 @@
 #define ZIP_ER_ENCRNOTSUPP   24  /* N Encryption method not supported */
 #define ZIP_ER_RDONLY        25  /* N Read-only archive */ 
 #define ZIP_ER_NOPASSWD      26  /* N No password provided */
-
+#define ZIP_ER_WRONGPASSWD   27  /* N Wrong password provided */
 
 /* type of system error value */
 
@@ -165,9 +171,6 @@
     ZIP_SOURCE_FREE	/* cleanup and free resources */
 };
 
-typedef ssize_t (*zip_source_callback)(void *state, void *data,
-				       size_t len, enum zip_source_cmd cmd);
-
 struct zip_stat {
     const char *name;			/* name of the file */
     int index;				/* index within archive */
@@ -183,6 +186,16 @@
 struct zip_file;
 struct zip_source;
 
+typedef ssize_t (*zip_source_callback)(void *state, void *data,
+				       size_t len, enum zip_source_cmd cmd);
+typedef struct zip_source *(*zip_compression_implementation)(struct zip *,
+						     struct zip_source *,
+						     zip_uint16_t, int);
+typedef struct zip_source *(*zip_encryption_implementation)(struct zip *,
+						    struct zip_source *,
+						    zip_uint16_t, int,
+						    const char *);
+
 
 
 ZIP_EXTERN int zip_add(struct zip *, const char *, struct zip_source *);
@@ -199,10 +212,18 @@
 ZIP_EXTERN void zip_file_error_get(struct zip_file *, int *, int *);
 ZIP_EXTERN const char *zip_file_strerror(struct zip_file *);
 ZIP_EXTERN struct zip_file *zip_fopen(struct zip *, const char *, int);
+ZIP_EXTERN struct zip_file *zip_fopen_encrypted(struct zip *, const char *,
+						int, const char *);
 ZIP_EXTERN struct zip_file *zip_fopen_index(struct zip *, int, int);
+ZIP_EXTERN struct zip_file *zip_fopen_index_encrypted(struct zip *, int, int,
+						      const char *);
 ZIP_EXTERN ssize_t zip_fread(struct zip_file *, void *, size_t);
 ZIP_EXTERN const char *zip_get_archive_comment(struct zip *, int *, int);
 ZIP_EXTERN int zip_get_archive_flag(struct zip *, int, int);
+ZIP_EXTERN zip_compression_implementation zip_get_compression_implementation(
+    zip_uint16_t);
+ZIP_EXTERN zip_encryption_implementation zip_get_encryption_implementation(
+    zip_uint16_t);
 ZIP_EXTERN const char *zip_get_file_comment(struct zip *, int, int *, int);
 ZIP_EXTERN const char *zip_get_name(struct zip *, int, int);
 ZIP_EXTERN int zip_get_num_files(struct zip *);
@@ -215,6 +236,11 @@
 ZIP_EXTERN int zip_set_file_comment(struct zip *, int, const char *, int);
 ZIP_EXTERN struct zip_source *zip_source_buffer(struct zip *, const void *,
 						zip_uint64_t, int);
+ZIP_EXTERN ssize_t zip_source_call(struct zip_source *, void *, size_t,
+				   enum zip_source_cmd);
+ZIP_EXTERN struct zip_source *zip_source_deflate(struct zip *,
+						 struct zip_source *,
+						 zip_uint16_t, int);
 ZIP_EXTERN struct zip_source *zip_source_file(struct zip *, const char *,
 					      zip_uint64_t, zip_int64_t);
 ZIP_EXTERN struct zip_source *zip_source_filep(struct zip *, FILE *,
@@ -222,6 +248,10 @@
 ZIP_EXTERN void zip_source_free(struct zip_source *);
 ZIP_EXTERN struct zip_source *zip_source_function(struct zip *,
 						  zip_source_callback, void *);
+ZIP_EXTERN struct zip_source *zip_source_pkware(struct zip *,
+						struct zip_source *,
+						zip_uint16_t, int,
+						const char *);
 ZIP_EXTERN struct zip_source *zip_source_zip(struct zip *, struct zip *,
 				     int, int, zip_uint64_t, zip_int64_t);
 ZIP_EXTERN int zip_stat(struct zip *, const char *, int, struct zip_stat *);
diff --git a/lib/zip_fclose.c b/lib/zip_fclose.c
index 8709362..66d74e4 100644
--- a/lib/zip_fclose.c
+++ b/lib/zip_fclose.c
@@ -44,10 +44,8 @@
 {
     int i, ret;
     
-    if (zf->zstr)
-	inflateEnd(zf->zstr);
-    free(zf->buffer);
-    free(zf->zstr);
+    if (zf->src)
+	zip_source_free(zf->src);
 
     for (i=0; i<zf->za->nfile; i++) {
 	if (zf->za->file[i] == zf) {
diff --git a/lib/zip_fopen.c b/lib/zip_fopen.c
index 6e2f724..5a65eba 100644
--- a/lib/zip_fopen.c
+++ b/lib/zip_fopen.c
@@ -1,6 +1,6 @@
 /*
   zip_fopen.c -- open file in zip archive for reading
-  Copyright (C) 1999-2007 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 <libzip@nih.at>
@@ -45,5 +45,5 @@
     if ((idx=zip_name_locate(za, fname, flags)) < 0)
 	return NULL;
 
-    return zip_fopen_index(za, idx, flags);
+    return zip_fopen_index_encrypted(za, idx, flags, za->default_password);
 }
diff --git a/lib/zip_fopen_encrypted.c b/lib/zip_fopen_encrypted.c
new file mode 100644
index 0000000..0a4b131
--- /dev/null
+++ b/lib/zip_fopen_encrypted.c
@@ -0,0 +1,50 @@
+/*
+  zip_fopen_encrypted.c -- open file for reading with password
+  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 <libzip@nih.at>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include "zipint.h"
+
+
+
+ZIP_EXTERN struct zip_file *
+zip_fopen_encrypted(struct zip *za, const char *fname, int flags,
+		    const char *password)
+{
+    int idx;
+
+    if ((idx=zip_name_locate(za, fname, flags)) < 0)
+	return NULL;
+
+    return zip_fopen_index_encrypted(za, idx, flags, password);
+}
diff --git a/lib/zip_fopen_index.c b/lib/zip_fopen_index.c
index 7f2d90a..95be69e 100644
--- a/lib/zip_fopen_index.c
+++ b/lib/zip_fopen_index.c
@@ -39,183 +39,10 @@
 
 #include "zipint.h"
 
-static struct zip_file *_zip_file_new(struct zip *za);
-
 
 
 ZIP_EXTERN struct zip_file *
 zip_fopen_index(struct zip *za, int fileno, int flags)
 {
-    int len, ret;
-    int zfflags;
-    struct zip_file *zf;
-
-    if ((fileno < 0) || (fileno >= za->nentry)) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
-	return NULL;
-    }
-
-    if ((flags & ZIP_FL_UNCHANGED) == 0
-	&& ZIP_ENTRY_DATA_CHANGED(za->entry+fileno)) {
-	_zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
-	return NULL;
-    }
-
-    if (fileno >= za->cdir->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
-	return NULL;
-    }
-
-    if (za->cdir->entry[fileno].bitflags & ZIP_GPBF_ENCRYPTED) {
-	_zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
-	return NULL;
-    }
-
-    zfflags = 0;
-    switch (za->cdir->entry[fileno].comp_method) {
-    case ZIP_CM_STORE:
-	zfflags |= ZIP_ZF_CRC;
-	break;
-
-    case ZIP_CM_DEFLATE:
-	if ((flags & ZIP_FL_COMPRESSED) == 0)
-	    zfflags |= ZIP_ZF_CRC | ZIP_ZF_DECOMP;
-	break;
-    default:
-	if ((flags & ZIP_FL_COMPRESSED) == 0) {
-	    _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
-	    return NULL;
-	}
-	break;
-    }
-
-    zf = _zip_file_new(za);
-
-    zf->flags = zfflags;
-    /* zf->name = za->cdir->entry[fileno].filename; */
-    zf->method = za->cdir->entry[fileno].comp_method;
-    zf->bytes_left = za->cdir->entry[fileno].uncomp_size;
-    zf->cbytes_left = za->cdir->entry[fileno].comp_size;
-    zf->crc_orig = za->cdir->entry[fileno].crc;
-
-    if ((zf->fpos=_zip_file_get_offset(za, fileno)) == 0) {
-	zip_fclose(zf);
-	return NULL;
-    }
-    
-    if ((zf->flags & ZIP_ZF_DECOMP) == 0)
-	zf->bytes_left = zf->cbytes_left;
-    else {
-	if ((zf->buffer=(char *)malloc(BUFSIZE)) == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-	    zip_fclose(zf);
-	    return NULL;
-	}
-
-	len = _zip_file_fillbuf(zf->buffer, BUFSIZE, zf);
-	if (len <= 0) {
-	    _zip_error_copy(&za->error, &zf->error);
-	    zip_fclose(zf);
-	return NULL;
-	}
-
-	if ((zf->zstr = (z_stream *)malloc(sizeof(z_stream))) == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-	    zip_fclose(zf);
-	    return NULL;
-	}
-	zf->zstr->zalloc = Z_NULL;
-	zf->zstr->zfree = Z_NULL;
-	zf->zstr->opaque = NULL;
-	zf->zstr->next_in = (Bytef *)zf->buffer;
-	zf->zstr->avail_in = len;
-	
-	/* negative value to tell zlib that there is no header */
-	if ((ret=inflateInit2(zf->zstr, -MAX_WBITS)) != Z_OK) {
-	    _zip_error_set(&za->error, ZIP_ER_ZLIB, ret);
-	    zip_fclose(zf);
-	    return NULL;
-	}
-    }
-    
-    return zf;
-}
-
-
-
-int
-_zip_file_fillbuf(void *buf, size_t buflen, struct zip_file *zf)
-{
-    int i, j;
-
-    if (zf->error.zip_err != ZIP_ER_OK)
-	return -1;
-
-    if ((zf->flags & ZIP_ZF_EOF) || zf->cbytes_left <= 0 || buflen <= 0)
-	return 0;
-    
-    if (fseeko(zf->za->zp, zf->fpos, SEEK_SET) < 0) {
-	_zip_error_set(&zf->error, ZIP_ER_SEEK, errno);
-	return -1;
-    }
-    if (buflen < zf->cbytes_left)
-	i = buflen;
-    else
-	i = zf->cbytes_left;
-
-    j = fread(buf, 1, i, zf->za->zp);
-    if (j == 0) {
-	_zip_error_set(&zf->error, ZIP_ER_EOF, 0);
-	j = -1;
-    }
-    else if (j < 0)
-	_zip_error_set(&zf->error, ZIP_ER_READ, errno);
-    else {
-	zf->fpos += j;
-	zf->cbytes_left -= j;
-    }
-
-    return j;	
-}
-
-
-
-static struct zip_file *
-_zip_file_new(struct zip *za)
-{
-    struct zip_file *zf, **file;
-    int n;
-
-    if ((zf=(struct zip_file *)malloc(sizeof(struct zip_file))) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-	return NULL;
-    }
-    
-    if (za->nfile >= za->nfile_alloc-1) {
-	n = za->nfile_alloc + 10;
-	file = (struct zip_file **)realloc(za->file,
-					   n*sizeof(struct zip_file *));
-	if (file == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-	    free(zf);
-	    return NULL;
-	}
-	za->nfile_alloc = n;
-	za->file = file;
-    }
-
-    za->file[za->nfile++] = zf;
-
-    zf->za = za;
-    _zip_error_init(&zf->error);
-    zf->flags = 0;
-    zf->crc = crc32(0L, Z_NULL, 0);
-    zf->crc_orig = 0;
-    zf->method = -1;
-    zf->bytes_left = zf->cbytes_left = 0;
-    zf->fpos = 0;
-    zf->buffer = NULL;
-    zf->zstr = NULL;
-
-    return zf;
+    return zip_fopen_index_encrypted(za, fileno, flags, za->default_password);
 }
diff --git a/lib/zip_fopen_index_encrypted.c b/lib/zip_fopen_index_encrypted.c
new file mode 100644
index 0000000..ace0a5d
--- /dev/null
+++ b/lib/zip_fopen_index_encrypted.c
@@ -0,0 +1,179 @@
+/*
+  zip_fopen_index_encrypted.c -- open file for reading by index w/ password
+  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 <libzip@nih.at>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "zipint.h"
+
+static struct zip_file *_zip_file_new(struct zip *za);
+
+
+
+ZIP_EXTERN struct zip_file *
+zip_fopen_index_encrypted(struct zip *za, int fileno, int flags,
+			  const char *password)
+{
+    int zfflags;
+    struct zip_file *zf;
+    zip_compression_implementation comp_impl;
+    zip_encryption_implementation enc_impl;
+    struct zip_source *src, *s2;
+    zip_uint64_t start;
+    struct zip_stat st;
+
+    if ((fileno < 0) || (fileno >= za->nentry)) {
+	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	return NULL;
+    }
+
+    if ((flags & ZIP_FL_UNCHANGED) == 0
+	&& ZIP_ENTRY_DATA_CHANGED(za->entry+fileno)) {
+	_zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
+	return NULL;
+    }
+
+    if (fileno >= za->cdir->nentry) {
+	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	return NULL;
+    }
+
+    if (flags & ZIP_FL_ENCRYPTED)
+	flags |= ZIP_FL_COMPRESSED;
+
+    zip_stat_index(za, fileno, flags, &st);
+
+    zfflags = 0;
+    if (st.comp_method == ZIP_CM_STORE || (flags & ZIP_FL_COMPRESSED) == 0)
+	zfflags |= ZIP_ZF_CRC;
+
+    enc_impl = NULL;
+    if ((flags & ZIP_FL_ENCRYPTED) == 0) {
+	if (st.encryption_method != ZIP_EM_NONE) {
+	    if ((enc_impl=zip_get_encryption_implementation(
+		     st.encryption_method)) == NULL) {
+		_zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
+		return NULL;
+	    }
+	}
+    }
+
+    comp_impl = NULL;
+    if ((flags & ZIP_FL_COMPRESSED) == 0) {
+	if (st.comp_method != ZIP_CM_STORE) {
+	    if ((comp_impl=zip_get_compression_implementation(
+		     st.comp_method)) == NULL) {
+		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
+		return NULL;
+	    }
+	}
+    }
+
+    if ((start=_zip_file_get_offset(za, fileno)) == 0)
+	return NULL;
+
+    if ((src=_zip_source_file_or_p(za, NULL, za->zp, start, st.comp_size,
+				   0, &st)) == NULL)
+	return NULL;
+    if (enc_impl) {
+	if ((s2=enc_impl(za, src, ZIP_EM_TRAD_PKWARE, 0, password)) == NULL) {
+	    zip_source_free(src);
+	    return NULL;
+	}
+	src = s2;
+    }
+    if (comp_impl) {
+	if ((s2=comp_impl(za, src, za->cdir->entry[fileno].comp_method,
+			  0)) == NULL) {
+	    zip_source_free(src);
+	    return NULL;
+	}
+	src = s2;
+    }
+
+    if (zip_source_call(src, NULL, 0, ZIP_SOURCE_OPEN) < 0) {
+	zip_source_free(src);
+	return NULL;
+    }
+
+    zf = _zip_file_new(za);
+
+    zf->flags = zfflags;
+    zf->bytes_left = za->cdir->entry[fileno].uncomp_size;
+    zf->crc_orig = za->cdir->entry[fileno].crc;
+    zf->src = src;
+
+    return zf;
+}
+
+
+
+static struct zip_file *
+_zip_file_new(struct zip *za)
+{
+    struct zip_file *zf, **file;
+    int n;
+
+    if ((zf=(struct zip_file *)malloc(sizeof(struct zip_file))) == NULL) {
+	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	return NULL;
+    }
+    
+    if (za->nfile >= za->nfile_alloc-1) {
+	n = za->nfile_alloc + 10;
+	file = (struct zip_file **)realloc(za->file,
+					   n*sizeof(struct zip_file *));
+	if (file == NULL) {
+	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	    free(zf);
+	    return NULL;
+	}
+	za->nfile_alloc = n;
+	za->file = file;
+    }
+
+    za->file[za->nfile++] = zf;
+
+    zf->za = za;
+    _zip_error_init(&zf->error);
+    zf->flags = 0;
+    zf->crc = crc32(0L, Z_NULL, 0);
+    zf->crc_orig = 0;
+    zf->bytes_left = 0;
+    zf->src = NULL;
+
+    return zf;
+}
diff --git a/lib/zip_fread.c b/lib/zip_fread.c
index 3d9d3c2..4146b68 100644
--- a/lib/zip_fread.c
+++ b/lib/zip_fread.c
@@ -40,9 +40,7 @@
 ZIP_EXTERN ssize_t
 zip_fread(struct zip_file *zf, void *outbuf, size_t toread)
 {
-    int ret;
-    size_t out_before, len;
-    int i;
+    ssize_t n;
 
     if (!zf)
 	return -1;
@@ -53,70 +51,34 @@
     if ((zf->flags & ZIP_ZF_EOF) || (toread == 0))
 	return 0;
 
-    if (zf->bytes_left == 0) {
+    if ((n=zip_source_call(zf->src, outbuf, toread, ZIP_SOURCE_READ)) < 0) {
+	int e[2];
+	zip_source_call(zf->src, e, sizeof(e), ZIP_SOURCE_ERROR);
+	_zip_error_set(&zf->error, e[0], e[1]);
+	return -1;
+    }
+
+    if (n == 0) {
 	zf->flags |= ZIP_ZF_EOF;
+
 	if (zf->flags & ZIP_ZF_CRC) {
+	    if (zf->bytes_left != 0) {
+		_zip_error_set(&zf->error, ZIP_ER_INCONS, 0);
+		return -1;
+	    }
+
 	    if (zf->crc != zf->crc_orig) {
 		_zip_error_set(&zf->error, ZIP_ER_CRC, 0);
 		return -1;
 	    }
 	}
-	return 0;
     }
-    
-    if ((zf->flags & ZIP_ZF_DECOMP) == 0) {
-	ret = _zip_file_fillbuf(outbuf, toread, zf);
-	if (ret > 0) {
-	    if (zf->flags & ZIP_ZF_CRC)
-		zf->crc = crc32(zf->crc, (Bytef *)outbuf, ret);
-	    zf->bytes_left -= ret;
-	}
-	return ret;
-    }
-    
-    zf->zstr->next_out = (Bytef *)outbuf;
-    zf->zstr->avail_out = toread;
-    out_before = zf->zstr->total_out;
-    
-    /* endless loop until something has been accomplished */
-    for (;;) {
-	ret = inflate(zf->zstr, Z_SYNC_FLUSH);
-
-	switch (ret) {
-	case Z_OK:
-	case Z_STREAM_END:
-	    /* all ok */
-	    /* Z_STREAM_END probably won't happen, since we didn't
-	       have a header */
-	    len = zf->zstr->total_out - out_before;
-	    if (len >= zf->bytes_left || len >= toread) {
-		if (zf->flags & ZIP_ZF_CRC)
-		    zf->crc = crc32(zf->crc, (Bytef *)outbuf, len);
-		zf->bytes_left -= len;
-	        return len;
-	    }
-	    break;
-
-	case Z_BUF_ERROR:
-	    if (zf->zstr->avail_in == 0) {
-		i = _zip_file_fillbuf(zf->buffer, BUFSIZE, zf);
-		if (i == 0) {
-		    _zip_error_set(&zf->error, ZIP_ER_INCONS, 0);
-		    return -1;
-		}
-		else if (i < 0)
-		    return -1;
-		zf->zstr->next_in = (Bytef *)zf->buffer;
-		zf->zstr->avail_in = i;
-		continue;
-	    }
-	    /* fallthrough */
-	case Z_NEED_DICT:
-	case Z_DATA_ERROR:
-	case Z_STREAM_ERROR:
-	case Z_MEM_ERROR:
-	    _zip_error_set(&zf->error, ZIP_ER_ZLIB, ret);
-	    return -1;
+    else {
+	if (zf->flags & ZIP_ZF_CRC) {
+	    zf->bytes_left -= n;
+	    zf->crc = crc32(zf->crc, (Bytef *)outbuf, n);
 	}
     }
+
+    return n;
 }
diff --git a/lib/zip_get_compression_implementation.c b/lib/zip_get_compression_implementation.c
new file mode 100644
index 0000000..8d4ccb6
--- /dev/null
+++ b/lib/zip_get_compression_implementation.c
@@ -0,0 +1,46 @@
+/*
+  zip_get_compression_implementation.c -- get compression implementation
+  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  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
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include "zipint.h"
+
+
+
+ZIP_EXTERN zip_compression_implementation
+zip_get_compression_implementation(zip_uint16_t cm)
+{
+    if (cm == ZIP_CM_DEFLATE)
+	return zip_source_deflate;
+    return NULL;
+}
diff --git a/lib/zip_get_encryption_implementation.c b/lib/zip_get_encryption_implementation.c
new file mode 100644
index 0000000..d83698c
--- /dev/null
+++ b/lib/zip_get_encryption_implementation.c
@@ -0,0 +1,46 @@
+/*
+  zip_get_encryption_implementation.c -- get encryption implementation
+  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  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
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include "zipint.h"
+
+
+
+ZIP_EXTERN zip_encryption_implementation
+zip_get_encryption_implementation(zip_uint16_t em)
+{
+    if (em == ZIP_EM_TRAD_PKWARE)
+	return zip_source_pkware;
+    return NULL;
+}
diff --git a/lib/zip_new.c b/lib/zip_new.c
index 3e8ccee..7ce1237 100644
--- a/lib/zip_new.c
+++ b/lib/zip_new.c
@@ -65,6 +65,7 @@
     za->nfile = za->nfile_alloc = 0;
     za->file = NULL;
     za->flags = za->ch_flags = 0;
+    za->default_password = NULL;
     
     return za;
 }
diff --git a/lib/zip_set_archive_flag.c b/lib/zip_set_archive_flag.c
index dc3ef94..e0e713a 100644
--- a/lib/zip_set_archive_flag.c
+++ b/lib/zip_set_archive_flag.c
@@ -40,7 +40,7 @@
 ZIP_EXTERN int
 zip_set_archive_flag(struct zip *za, int flag, int value)
 {
-    int new_flags;
+    unsigned int new_flags;
     
     if (value)
 	new_flags = za->ch_flags | flag;
diff --git a/lib/zip_source_call.c b/lib/zip_source_call.c
new file mode 100644
index 0000000..c07979b
--- /dev/null
+++ b/lib/zip_source_call.c
@@ -0,0 +1,44 @@
+/*
+  zip_source_deflate.c -- deflate (de)compressoin routines
+  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  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
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include "zipint.h"
+
+
+
+ZIP_EXTERN ssize_t zip_source_call(struct zip_source *src, void *data,
+				   size_t len, enum zip_source_cmd cmd)
+{
+    return src->f(src->ud, data, len, cmd);
+}
diff --git a/lib/zip_source_deflate.c b/lib/zip_source_deflate.c
new file mode 100644
index 0000000..04efd4b
--- /dev/null
+++ b/lib/zip_source_deflate.c
@@ -0,0 +1,408 @@
+/*
+  zip_source_deflate.c -- deflate (de)compressoin routines
+  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  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
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "zipint.h"
+
+struct deflate {
+    struct zip_source *src;
+    int e[2];
+
+    int eof;
+    int mem_level;
+    zip_uint64_t size;
+    zip_uint32_t crc;
+    char buffer[BUFSIZE];
+    z_stream zstr;
+};
+
+static ssize_t compress_read(struct deflate *, void *, size_t);
+static ssize_t decompress_read(struct deflate *, void *, size_t);
+static ssize_t deflate_compress(void *, void *, size_t, enum zip_source_cmd);
+static ssize_t deflate_decompress(void *, void *, size_t, enum zip_source_cmd);
+
+
+
+ZIP_EXTERN struct zip_source *
+zip_source_deflate(struct zip *za, struct zip_source *src,
+		   zip_uint16_t cm, int flags)
+{
+    struct deflate *ctx;
+
+    if (za == NULL || src == NULL || cm != ZIP_CM_DEFLATE) {
+	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	return NULL;
+    }
+
+    if ((ctx=(struct deflate *)malloc(sizeof(*ctx))) == NULL) {
+	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	return NULL;
+    }
+
+    ctx->src = src;
+    ctx->e[0] = ctx->e[1] = 0;
+    ctx->eof = 0;
+    if (flags & ZIP_CODEC_ENCODE) {
+	if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
+	    ctx->mem_level = TORRENT_MEM_LEVEL;
+	else
+	    ctx->mem_level = MAX_MEM_LEVEL;
+    }
+
+    return zip_source_function(za, ((flags & ZIP_CODEC_ENCODE)
+				    ? deflate_compress : deflate_decompress),
+			       ctx);
+}
+
+
+
+static ssize_t
+compress_read(struct deflate *ctx, void *data, size_t len)
+{
+    int end, ret;
+    ssize_t n;
+    size_t out_before;
+
+    if (ctx->e[0] != 0)
+	return -1;
+    
+    if (ctx->eof || (len == 0))
+	return 0;
+	
+    ctx->zstr.next_out = (Bytef *)data;
+    ctx->zstr.avail_out = len;
+    out_before = ctx->zstr.total_out;
+
+    end = 0;
+    while (!end) {
+	ret = deflate(&ctx->zstr, ctx->eof ? Z_FINISH : 0);
+
+	switch (ret) {
+	case Z_OK:
+	case Z_STREAM_END:
+	    /* all ok */
+
+	    if (ctx->zstr.total_out - out_before >= len)
+		end = 1;
+	    break;
+
+	case Z_BUF_ERROR:
+	    if (ctx->zstr.avail_in == 0) {
+		if (ctx->eof) {
+		    end = 1;
+		    break;
+		}
+
+		if ((n=zip_source_call(ctx->src, ctx->buffer,
+			    sizeof(ctx->buffer), ZIP_SOURCE_READ)) < 0) {
+		    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e),
+				 ZIP_SOURCE_ERROR);
+		    end = 1;
+		    break;
+		}
+		else if (n == 0) {
+		    ctx->eof = 1;
+		    ctx->size = ctx->zstr.total_in;
+		    /* XXX: check against stat of src? */
+		}
+		else {
+		    ctx->crc = crc32(ctx->crc, (Bytef *)ctx->buffer, n);
+		    ctx->zstr.next_in = (Bytef *)ctx->buffer;
+		    ctx->zstr.avail_in = n;
+		}
+		continue;
+	    }
+	    /* fallthrough */
+	case Z_NEED_DICT:
+	case Z_DATA_ERROR:
+	case Z_STREAM_ERROR:
+	case Z_MEM_ERROR:
+	    ctx->e[0] = ZIP_ER_ZLIB;
+	    ctx->e[1] = ret;
+
+	    end = 1;
+	    break;
+	}
+    }
+
+    if (ctx->zstr.total_out > out_before)
+	return ctx->zstr.total_out - out_before;
+
+    return (ctx->e[0] == 0) ? 0 : -1;
+}
+
+
+
+static ssize_t
+decompress_read(struct deflate *ctx, void *data, size_t len)
+{
+    int end, ret;
+    ssize_t n;
+
+    if (ctx->e[0] != 0)
+	return -1;
+    
+    if (ctx->eof || (len == 0))
+	return 0;
+	
+    ctx->zstr.next_out = (Bytef *)data;
+    ctx->zstr.avail_out = len;
+
+    end = 0;
+    while (!end && ctx->zstr.avail_out) {
+	ret = inflate(&ctx->zstr, Z_SYNC_FLUSH);
+
+	switch (ret) {
+	case Z_OK:
+	    break;
+	    
+	case Z_STREAM_END:
+	    ctx->eof = 1;
+	    end = 1;
+	    break;
+
+	case Z_BUF_ERROR:
+	    if (ctx->zstr.avail_in == 0) {
+		if ((n=zip_source_call(ctx->src, ctx->buffer,
+			    sizeof(ctx->buffer), ZIP_SOURCE_READ)) < 0) {
+		    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e),
+				    ZIP_SOURCE_ERROR);
+		    end = 1;
+		    break;
+		}
+		else if (n == 0)
+		    ctx->eof = 1;
+		else {
+		    ctx->zstr.next_in = (Bytef *)ctx->buffer;
+		    ctx->zstr.avail_in = n;
+		}
+		continue;
+	    }
+	    /* fallthrough */
+	case Z_NEED_DICT:
+	case Z_DATA_ERROR:
+	case Z_STREAM_ERROR:
+	case Z_MEM_ERROR:
+	    ctx->e[0] = ZIP_ER_ZLIB;
+	    ctx->e[1] = ret;
+	    end = 1;
+	    break;
+	}
+    }
+
+    if (ctx->zstr.avail_out < len)
+	return len - ctx->zstr.avail_out;
+
+    return (ctx->e[0] == 0) ? 0 : -1;
+}
+
+
+
+static ssize_t
+deflate_compress(void *ud, void *data, size_t len, enum zip_source_cmd cmd)
+{
+    struct deflate *ctx;
+    int ret;
+
+    ctx = (struct deflate *)ud;
+
+    switch (cmd) {
+    case ZIP_SOURCE_OPEN:
+	
+	ctx->zstr.zalloc = Z_NULL;
+	ctx->zstr.zfree = Z_NULL;
+	ctx->zstr.opaque = NULL;
+	ctx->zstr.avail_in = 0;
+	ctx->zstr.avail_out = 0;
+
+	/* negative value to tell zlib not to write a header */
+	if ((ret=deflateInit2(&ctx->zstr, Z_BEST_COMPRESSION, Z_DEFLATED,
+			      -MAX_WBITS, ctx->mem_level,
+			      Z_DEFAULT_STRATEGY)) != Z_OK) {
+	    ctx->e[0] = ZIP_ER_ZLIB;
+	    ctx->e[1] = ret;
+	    return -1;
+
+	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+
+	return 0;
+    }
+
+    case ZIP_SOURCE_READ:
+	return compress_read(ctx, data, len);
+
+    case ZIP_SOURCE_CLOSE:
+	deflateEnd(&ctx->zstr);
+
+	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+	return 0;
+
+    case ZIP_SOURCE_STAT:
+	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+	else {
+	    struct zip_stat *st;
+
+	    st = (struct zip_stat *)data;
+
+	    st->comp_method = ZIP_CM_DEFLATE;
+	    if (ctx->eof) {
+		st->comp_size = ctx->size;
+		st->crc = ctx->crc;
+	    }
+	    else
+		st->comp_size = -1;
+	}
+	return 0;
+
+    case ZIP_SOURCE_ERROR:
+	if (len < sizeof(int)*2)
+	    return -1;
+
+	memcpy(data, ctx->e, sizeof(int)*2);
+	return sizeof(int)*2;
+
+    case ZIP_SOURCE_FREE:
+	/* XXX: deflateEnd if close was not called */
+	zip_source_call(ctx->src, data, len, cmd);
+	free(ctx);
+	return 0;
+
+    default:
+	ctx->e[0] = ZIP_ER_INVAL;
+	ctx->e[1] = 0;
+	return -1;
+    }
+    
+}
+
+
+
+static ssize_t
+deflate_decompress(void *ud, void *data, size_t len, enum zip_source_cmd cmd)
+{
+    struct deflate *ctx;
+    ssize_t n;
+    int ret;
+
+    ctx = (struct deflate *)ud;
+
+    switch (cmd) {
+    case ZIP_SOURCE_OPEN:
+	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+
+	if ((n=zip_source_call(ctx->src, ctx->buffer, sizeof(ctx->buffer),
+			    ZIP_SOURCE_READ)) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+
+	ctx->zstr.zalloc = Z_NULL;
+	ctx->zstr.zfree = Z_NULL;
+	ctx->zstr.opaque = NULL;
+	ctx->zstr.next_in = (Bytef *)ctx->buffer;
+	ctx->zstr.avail_in = n;
+
+	/* negative value to tell zlib that there is no header */
+	if ((ret=inflateInit2(&ctx->zstr, -MAX_WBITS)) != Z_OK) {
+	    ctx->e[0] = ZIP_ER_ZLIB;
+	    ctx->e[1] = ret;
+
+	    zip_source_call(ctx->src, NULL, 0, ZIP_SOURCE_CLOSE);
+	    return -1;
+	}
+	return 0;
+
+    case ZIP_SOURCE_READ:
+	return decompress_read(ctx, data, len);
+
+    case ZIP_SOURCE_CLOSE:
+	inflateEnd(&ctx->zstr);
+
+	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+	return 0;
+
+    case ZIP_SOURCE_STAT:
+	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+	else {
+	    struct zip_stat *st;
+
+	    st = (struct zip_stat *)data;
+
+	    st->comp_method = ZIP_CM_STORE;
+	    if (st->comp_size > 0 && st->size > 0)
+		st->comp_size = st->size;
+	}
+	return 0;
+
+    case ZIP_SOURCE_ERROR:
+	if (len < sizeof(int)*2)
+	    return -1;
+
+	memcpy(data, ctx->e, sizeof(int)*2);
+	return sizeof(int)*2;
+
+    case ZIP_SOURCE_FREE:
+	/* XXX: inflateEnd if close was not called */
+	zip_source_call(ctx->src, data, len, cmd);
+	free(ctx);
+	return 0;
+
+    default:
+	ctx->e[0] = ZIP_ER_INVAL;
+	ctx->e[1] = 0;
+	return -1;
+    }
+    
+}
diff --git a/lib/zip_source_file.c b/lib/zip_source_file.c
index 973f133..79c8ee5 100644
--- a/lib/zip_source_file.c
+++ b/lib/zip_source_file.c
@@ -1,6 +1,6 @@
 /*
   zip_source_file.c -- create data source from file
-  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 <libzip@nih.at>
@@ -52,5 +52,5 @@
 	return NULL;
     }
 
-    return _zip_source_file_or_p(za, fname, NULL, start, len);
+    return _zip_source_file_or_p(za, fname, NULL, start, len, 1, NULL);
 }
diff --git a/lib/zip_source_filep.c b/lib/zip_source_filep.c
index 6d13972..7ca0704 100644
--- a/lib/zip_source_filep.c
+++ b/lib/zip_source_filep.c
@@ -1,6 +1,6 @@
 /*
   zip_source_filep.c -- create data source from FILE *
-  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 <libzip@nih.at>
@@ -44,6 +44,9 @@
 struct read_file {
     char *fname;	/* name of file to copy from */
     FILE *f;		/* file to copy from */
+    int closep;		/* close f */
+    struct zip_stat st;	/* stat information passed in */
+
     zip_uint64_t off;	/* start offset of */
     zip_int64_t len;	/* length of data to copy */
     zip_int64_t remain;	/* bytes remaining to be copied */
@@ -67,14 +70,15 @@
 	return NULL;
     }
 
-    return _zip_source_file_or_p(za, NULL, file, start, len);
+    return _zip_source_file_or_p(za, NULL, file, start, len, 1, NULL);
 }
 
 
 
 struct zip_source *
 _zip_source_file_or_p(struct zip *za, const char *fname, FILE *file,
-		      zip_uint64_t start, zip_int64_t len)
+		      zip_uint64_t start, zip_int64_t len, int closep,
+		      const struct zip_stat *st)
 {
     struct read_file *f;
     struct zip_source *zs;
@@ -100,7 +104,12 @@
     f->f = file;
     f->off = start;
     f->len = (len ? len : -1);
-    
+    f->closep = f->fname ? 1 : closep;
+    if (st)
+	memcpy(&f->st, st, sizeof(f->st));
+    else
+	zip_stat_init(&f->st);
+
     if ((zs=zip_source_function(za, read_file, f)) == NULL) {
 	free(f);
 	return NULL;
@@ -131,10 +140,12 @@
 	    }
 	}
 
-	if (fseeko(z->f, (off_t)z->off, SEEK_SET) < 0) {
-	    z->e[0] = ZIP_ER_SEEK;
-	    z->e[1] = errno;
-	    return -1;
+	if (z->closep) {
+	    if (fseeko(z->f, (off_t)z->off, SEEK_SET) < 0) {
+		z->e[0] = ZIP_ER_SEEK;
+		z->e[1] = errno;
+		return -1;
+	    }
 	}
 	z->remain = z->len;
 	return 0;
@@ -144,7 +155,17 @@
 	    n = len > z->remain ? z->remain : len;
 	else
 	    n = len;
-	
+
+	if (!z->closep) {
+	    /* we might share this file with others, so let's be save */
+	    if (fseeko(z->f, (off_t)(z->off + z->len-z->remain),
+		       SEEK_SET) < 0) {
+		z->e[0] = ZIP_ER_SEEK;
+		z->e[1] = errno;
+		return -1;
+	    }
+	}
+
 	if ((i=fread(buf, 1, n, z->f)) < 0) {
 	    z->e[0] = ZIP_ER_READ;
 	    z->e[1] = errno;
@@ -172,26 +193,29 @@
 	    if (len < sizeof(*st))
 		return -1;
 
-	    if (z->f)
-		err = fstat(fileno(z->f), &fst);
-	    else
-		err = stat(z->fname, &fst);
+	    if (z->st.size != -1)
+		memcpy(st, &z->st, sizeof(st));
+	    else {
+		if (z->f)
+		    err = fstat(fileno(z->f), &fst);
+		else
+		    err = stat(z->fname, &fst);
 
-	    if (err != 0) {
-		z->e[0] = ZIP_ER_READ; /* best match */
-		z->e[1] = errno;
-		return -1;
+		if (err != 0) {
+		    z->e[0] = ZIP_ER_READ; /* best match */
+		    z->e[1] = errno;
+		    return -1;
+		}
+
+		st = (struct zip_stat *)data;
+		
+		zip_stat_init(st);
+		st->mtime = fst.st_mtime;
+		if (z->len != -1)
+		    st->size = z->len;
+		else if ((fst.st_mode&S_IFMT) == S_IFREG)
+		    st->size = fst.st_size;
 	    }
-
-	    st = (struct zip_stat *)data;
-
-	    zip_stat_init(st);
-	    st->mtime = fst.st_mtime;
-	    if (z->len != -1)
-		st->size = z->len;
-	    else if ((fst.st_mode&S_IFMT) == S_IFREG)
-		st->size = fst.st_size;
-
 	    return sizeof(*st);
 	}
 
@@ -204,7 +228,7 @@
 
     case ZIP_SOURCE_FREE:
 	free(z->fname);
-	if (z->f)
+	if (z->closep && z->f)
 	    fclose(z->f);
 	free(z);
 	return 0;
diff --git a/lib/zip_source_pkware.c b/lib/zip_source_pkware.c
new file mode 100644
index 0000000..136f08c
--- /dev/null
+++ b/lib/zip_source_pkware.c
@@ -0,0 +1,223 @@
+/*
+  zip_source_pkware.c -- Traditional PKWARE de/encryption routines
+  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  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
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "zipint.h"
+
+struct trad_pkware {
+    struct zip_source *src;
+    int e[2];
+
+    zip_uint32_t key[3];
+};
+
+#define HEADERLEN	12
+#define KEY0		305419896
+#define KEY1		591751049
+#define KEY2		878082192
+
+
+
+static void decrypt(struct trad_pkware *, zip_uint8_t *,
+		    const zip_uint8_t *, size_t);
+static int decrypt_header(struct trad_pkware *);
+static ssize_t pkware_decrypt(void *, void *, size_t, enum zip_source_cmd);
+
+
+
+ZIP_EXTERN struct zip_source *
+zip_source_pkware(struct zip *za, struct zip_source *src,
+		  zip_uint16_t em, int flags, const char *password)
+{
+    struct trad_pkware *ctx;
+
+    if (za == NULL || password == NULL || src == NULL
+	|| em != ZIP_EM_TRAD_PKWARE || (flags & ZIP_CODEC_ENCODE) == 0) {
+	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	return NULL;
+    }
+
+    if ((ctx=(struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) {
+	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	return NULL;
+    }
+
+    ctx->src = src;
+    ctx->e[0] = ctx->e[1] = 0;
+
+    ctx->key[0] = KEY0;
+    ctx->key[1] = KEY1;
+    ctx->key[2] = KEY2;
+    decrypt(ctx, NULL, (const zip_uint8_t *)password, strlen(password));
+
+    return zip_source_function(za, pkware_decrypt, ctx);
+}
+
+
+
+static void
+decrypt(struct trad_pkware *ctx, zip_uint8_t *out, const zip_uint8_t *in,
+	size_t len)
+{
+    zip_uint16_t clr;
+    size_t i;
+    Bytef b;
+
+    for (i=0; i<len; i++) {
+	/* decrypt next byte */
+	clr = ctx->key[2] || 2;
+	clr = (clr * (clr ^ 1)) >> 8;
+	clr = in[i] ^ clr;
+
+	/* update keys */
+	b = clr;
+	ctx->key[0] = crc32(ctx->key[0], &b, 1);
+	ctx->key[1] = (ctx->key[1] + (ctx->key[0] & 0xff)) * 134775813 + 1;
+	b = ctx->key[1] >> 24;
+	ctx->key[2] = crc32(ctx->key[2], &b, 1);
+
+	/* store cleartext */
+	if (out)
+	    out[i] = clr;
+    }
+}
+
+
+
+static int
+decrypt_header(struct trad_pkware *ctx)
+{
+    zip_uint8_t header[HEADERLEN];
+    struct zip_stat st;
+    ssize_t n;
+
+    if ((n=zip_source_call(ctx->src, header, HEADERLEN, ZIP_SOURCE_READ)) < 0) {
+	zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	return -1;
+    }
+    
+    if (n != HEADERLEN) {
+	ctx->e[0] = ZIP_ER_EOF;
+	ctx->e[1] = 0;
+	return -1;
+    }
+
+    decrypt(ctx, header, header, HEADERLEN);
+
+    if (zip_source_call(ctx->src, &st, sizeof(st), ZIP_SOURCE_STAT) < 0
+	|| st.crc == 0) {
+	/* no CRC available, skip password validataion */
+	return 0;
+    }
+
+    if (header[HEADERLEN-1] != st.crc>>24) {
+	ctx->e[0] = ZIP_ER_WRONGPASSWD;
+	ctx->e[1] = 0;
+    }
+
+    return 0;
+}
+
+
+
+static ssize_t
+pkware_decrypt(void *ud, void *data, size_t len, enum zip_source_cmd cmd)
+{
+    struct trad_pkware *ctx;
+    ssize_t n;
+
+    ctx = (struct trad_pkware *)ud;
+
+    switch (cmd) {
+    case ZIP_SOURCE_OPEN:
+	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+	if (decrypt_header(ctx) < 0)
+	    return -1;
+	return 0;
+
+    case ZIP_SOURCE_READ:
+	if ((n=zip_source_call(ctx->src, data, len, cmd)) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+	decrypt(ud, (zip_uint8_t *)data, (zip_uint8_t *)data, (size_t)n);
+	return n;
+
+    case ZIP_SOURCE_CLOSE:
+	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+	return 0;
+
+    case ZIP_SOURCE_STAT:
+	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
+	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+	    return -1;
+	}
+	else {
+	    struct zip_stat *st;
+
+	    st = (struct zip_stat *)data;
+
+	    st->encryption_method = ZIP_EM_NONE;
+	    if (st->comp_size > 0)
+		st->comp_size -= HEADERLEN;
+	}
+	return 0;
+
+    case ZIP_SOURCE_ERROR:
+	if (len < sizeof(int)*2)
+	    return -1;
+
+	memcpy(data, ctx->e, sizeof(int)*2);
+	return sizeof(int)*2;
+
+    case ZIP_SOURCE_FREE:
+	zip_source_call(ctx->src, data, len, cmd);
+	free(ud);
+	return 0;
+
+    default:
+	ctx->e[0] = ZIP_ER_INVAL;
+	ctx->e[1] = 0;
+	return -1;
+    }
+}
diff --git a/lib/zipint.h b/lib/zipint.h
index 9f17dff..a047bcd 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -91,8 +91,7 @@
 /* constants for struct zip_file's member flags */
 
 #define ZIP_ZF_EOF	1 /* EOF reached */
-#define ZIP_ZF_DECOMP	2 /* decompress data */
-#define ZIP_ZF_CRC	4 /* compute and compare CRC */
+#define ZIP_ZF_CRC	2 /* compute and compare CRC */
 
 /* directory entry: general purpose bit flags */
 
@@ -118,6 +117,8 @@
     unsigned int flags;		/* archive global flags */
     unsigned int ch_flags;	/* changed archive global flags */
 
+    char *default_password;	/* password used when no other supplied */
+
     struct zip_cdir *cdir;	/* central directory */
     char *ch_comment;		/* changed archive comment */
     int ch_comment_len;		/* length of changed zip archive
@@ -137,16 +138,11 @@
     struct zip_error error;	/* error information */
     int flags;			/* -1: eof, >0: error */
 
-    int method;			/* compression method */
-    off_t fpos;			/* position within zip file (fread/fwrite) */
     unsigned long bytes_left;	/* number of bytes left to read */
-    unsigned long cbytes_left;  /* number of bytes of compressed data left */
-    
     unsigned long crc;		/* CRC so far */
     unsigned long crc_orig;	/* CRC recorded in archive */
-    
-    char *buffer;
-    z_stream *zstr;
+
+    struct zip_source *src;	/* data source */
 };
 
 /* zip archive directory entry (central or local) */
@@ -187,6 +183,7 @@
 
 
 struct zip_source {
+    struct zip_source *src;
     zip_source_callback f;
     void *ud;
 };
@@ -249,7 +246,8 @@
 struct zip *_zip_open(const char *, FILE *, int, int, int *);
 
 struct zip_source *_zip_source_file_or_p(struct zip *, const char *, FILE *,
-					 zip_uint64_t, zip_int64_t);
+					 zip_uint64_t, zip_int64_t, int,
+					 const struct zip_stat *);
 
 int _zip_changed(struct zip *, int *);
 void _zip_free(struct zip *);
diff --git a/regress/fread.c b/regress/fread.c
index 2be14b2..8417170 100644
--- a/regress/fread.c
+++ b/regress/fread.c
@@ -152,11 +152,14 @@
     if (when_got != when_ex || ze_got != ze_ex || se_got != se_ex) {
 	zip_error_to_str(expected, sizeof(expected), ze_ex, se_ex);
 	zip_error_to_str(got, sizeof(got), ze_got, se_got);
-	printf("%s: got %s error (%s), expected %s error (%s)\n", prg,
+	printf("%s: %s: got %s error (%s), expected %s error (%s)\n",
+	       prg, name,
 	       when_name[when_got], got, 
 	       when_name[when_ex], expected);
 	return 1;
     }
+    else if (getenv("VERBOSE"))
+	printf("%s: %s: passed\n", prg, name);
 
     return 0;
 }