Mercurial > dovecot > core-2.2
changeset 14739:378ba560ea9f
Moved zlib/bzlib code to lib-compression library.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 01 Aug 2012 18:30:40 +0300 |
parents | c3a781ef0aeb |
children | 7ece66101bbd |
files | Makefile.am configure.in dovecot-config.in.in dovecot.m4 src/Makefile.am src/lib-compression/Makefile.am src/lib-compression/compression.c src/lib-compression/compression.h src/lib-compression/istream-bzlib.c src/lib-compression/istream-zlib.c src/lib-compression/istream-zlib.h src/lib-compression/ostream-bzlib.c src/lib-compression/ostream-zlib.c src/lib-compression/ostream-zlib.h src/plugins/imap-zlib/Makefile.am src/plugins/imap-zlib/imap-zlib-plugin.c src/plugins/zlib/Makefile.am src/plugins/zlib/istream-bzlib.c src/plugins/zlib/istream-zlib.c src/plugins/zlib/istream-zlib.h src/plugins/zlib/ostream-bzlib.c src/plugins/zlib/ostream-zlib.c src/plugins/zlib/ostream-zlib.h src/plugins/zlib/zlib-plugin.c src/plugins/zlib/zlib-plugin.h |
diffstat | 25 files changed, 1611 insertions(+), 1571 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile.am Tue Jul 31 19:32:03 2012 +0300 +++ b/Makefile.am Wed Aug 01 18:30:40 2012 +0300 @@ -66,6 +66,7 @@ -e "s|^\(LIBDOVECOT_LOGIN\)=.*$$|\1=-ldovecot-login|" \ -e "s|^\(LIBDOVECOT_SQL\)=.*$$|\1=-ldovecot-sql|" \ -e "s|^\(LIBDOVECOT_SSL\)=.*$$|\1=-ldovecot-ssl|" \ + -e "s|^\(LIBDOVECOT_COMPRESS\)=.*$$|\1=-ldovecot-compress|" \ -e "s|^\(LIBDOVECOT_LDA\)=.*$$|\1=-ldovecot-lda|" \ -e "s|^\(LIBDOVECOT_STORAGE\)=.*$$|\1=-ldovecot-storage|" \ -e "s|^\(LIBDOVECOT_INCLUDE\)=.*$$|\1=-I$(pkgincludedir)|" \
--- a/configure.in Tue Jul 31 19:32:03 2012 +0300 +++ b/configure.in Wed Aug 01 18:30:40 2012 +0300 @@ -2515,6 +2515,7 @@ LIBDOVECOT_STORAGE_DEPS='$(top_builddir)/src/lib-storage/libdovecot-storage.la $(top_builddir)/src/lib-imap-storage/libimap-storage.la' LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/libdovecot-login.la' LIBDOVECOT_SSL='$(top_builddir)/src/lib-master/libmaster_ssl.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la' + LIBDOVECOT_COMPRESS='$(top_builddir)/src/lib-compression/libcompression.la' LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/libdovecot-lda.la' else LIBDOVECOT_DEPS='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-fs/libfs.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib/liblib.la' @@ -2524,6 +2525,7 @@ LIBDOVECOT_STORAGE_DEPS="$LIBDOVECOT_STORAGE_FIRST $LINKED_STORAGE_LIBS $LIBDOVECOT_STORAGE_LAST" LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/liblogin.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la' LIBDOVECOT_SSL='$(top_builddir)/src/lib-master/libmaster_ssl.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la' + LIBDOVECOT_COMPRESS='$(top_builddir)/src/lib-compression/libcompression.la' LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/liblda.la' fi LIBDOVECOT_STORAGE="$LIBDOVECOT_STORAGE_DEPS $LINKED_STORAGE_LDADD" @@ -2535,6 +2537,7 @@ AC_SUBST(LIBDOVECOT_LOGIN) AC_SUBST(LIBDOVECOT_SQL) AC_SUBST(LIBDOVECOT_SSL) +AC_SUBST(LIBDOVECOT_COMPRESS) AC_SUBST(LIBDOVECOT_LDA) dnl ** @@ -2576,25 +2579,27 @@ dnl ** Plugins dnl ** +COMPRESS_LIBS= if test "$want_zlib" != "no"; then AC_CHECK_HEADER(zlib.h, [ have_zlib=yes - have_zlib_plugin=yes + have_compress_lib=yes AC_DEFINE(HAVE_ZLIB,, Define if you have zlib library) + COMPRESS_LIBS="$COMPRESS_LIBS -lz" ], [ if test "$want_zlib" = "yes"; then AC_ERROR([Can't build with zlib support: zlib.h not found]) fi ]) fi -AM_CONDITIONAL(BUILD_ZLIB, test "$have_zlib" = "yes") if test "$want_bzlib" != "no"; then AC_CHECK_HEADER(bzlib.h, [ AC_CHECK_LIB(bz2, BZ2_bzdopen, [ have_bzlib=yes - have_zlib_plugin=yes + have_compress_lib=yes AC_DEFINE(HAVE_BZLIB,, Define if you have bzlib library) + COMPRESS_LIBS="$COMPRESS_LIBS -lbz2" ], [ if test "$want_bzlib" = "yes"; then AC_ERROR([Can't build with bzlib support: libbz2 not found]) @@ -2606,8 +2611,8 @@ fi ]) fi -AM_CONDITIONAL(BUILD_BZLIB, test "$have_bzlib" = "yes") -AM_CONDITIONAL(BUILD_ZLIB_PLUGIN, test "$have_zlib_plugin" = "yes") +AC_SUBST(COMPRESS_LIBS) +AM_CONDITIONAL(BUILD_ZLIB_PLUGIN, test "$have_compress_lib" = "yes") RPCGEN=${RPCGEN-rpcgen} if ! $RPCGEN -c /dev/null > /dev/null; then @@ -2747,6 +2752,7 @@ src/lib-sql/Makefile src/lib-auth/Makefile src/lib-charset/Makefile +src/lib-compression/Makefile src/lib-dict/Makefile src/lib-dns/Makefile src/lib-fs/Makefile
--- a/dovecot-config.in.in Tue Jul 31 19:32:03 2012 +0300 +++ b/dovecot-config.in.in Wed Aug 01 18:30:40 2012 +0300 @@ -2,11 +2,13 @@ DOVECOT_LIBS="@LIBS@" DOVECOT_SSL_LIBS="@SSL_LIBS@" DOVECOT_SQL_LIBS="@SQL_LIBS@" +DOVECOT_COMPRESS_LIBS="@COMPRESS_LIBS@" LIBDOVECOT="@LIBDOVECOT@ @MODULE_LIBS@" LIBDOVECOT_LOGIN="@LIBDOVECOT_LOGIN@ @SSL_LIBS@" LIBDOVECOT_SQL="@LIBDOVECOT_SQL@" LIBDOVECOT_SSL="@LIBDOVECOT_SSL@" +LIBDOVECOT_COMPRESS="@LIBDOVECOT_COMPRESS@" LIBDOVECOT_LDA="@LIBDOVECOT_LDA@" LIBDOVECOT_STORAGE="@LIBDOVECOT_STORAGE@" @@ -14,10 +16,11 @@ LIBDOVECOT_LOGIN_DEPS="@LIBDOVECOT_LOGIN@" LIBDOVECOT_SQL_DEPS="@LIBDOVECOT_SQL@" LIBDOVECOT_SSL_DEPS="@LIBDOVECOT_SSL@" +LIBDOVECOT_COMPRESS_DEPS="@LIBDOVECOT_COMPRESS@" LIBDOVECOT_LDA_DEPS="@LIBDOVECOT_LDA@" LIBDOVECOT_STORAGE_DEPS="@LIBDOVECOT_STORAGE_DEPS@" -LIBDOVECOT_INCLUDE="-I$(incdir) -I$(incdir)/src/lib -I$(incdir)/src/lib-dict -I$(incdir)/src/lib-dns -I$(incdir)/src/lib-mail -I$(incdir)/src/lib-imap -I$(incdir)/src/lib-fs -I$(incdir)/src/lib-charset -I$(incdir)/src/lib-auth -I$(incdir)/src/lib-master -I$(incdir)/src/lib-ssl-iostream -I$(incdir)/src/lib-settings" +LIBDOVECOT_INCLUDE="-I$(incdir) -I$(incdir)/src/lib -I$(incdir)/src/lib-dict -I$(incdir)/src/lib-dns -I$(incdir)/src/lib-mail -I$(incdir)/src/lib-imap -I$(incdir)/src/lib-fs -I$(incdir)/src/lib-charset -I$(incdir)/src/lib-auth -I$(incdir)/src/lib-master -I$(incdir)/src/lib-ssl-iostream -I$(incdir)/src/lib-compression -I$(incdir)/src/lib-settings" LIBDOVECOT_LDA_INCLUDE="-I$(incdir)/src/lib-lda -I$(incdir)/src/lda" LIBDOVECOT_STORAGE_INCLUDE="-I$(incdir)/src/lib-index -I$(incdir)/src/lib-storage -I$(incdir)/src/lib-storage/index -I$(incdir)/src/lib-storage/index/raw" LIBDOVECOT_LOGIN_INCLUDE="-I$(incdir)/src/login-common"
--- a/dovecot.m4 Tue Jul 31 19:32:03 2012 +0300 +++ b/dovecot.m4 Wed Aug 01 18:30:40 2012 +0300 @@ -6,7 +6,7 @@ # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. -# serial 7 +# serial 8 AC_DEFUN([DC_DOVECOT_MODULEDIR],[ AC_ARG_WITH(moduledir, @@ -84,9 +84,9 @@ fi AX_SUBST_L([DISTCHECK_CONFIGURE_FLAGS], [dovecotdir], [dovecot_moduledir], [dovecot_pkgincludedir], [dovecot_pkglibexecdir], [dovecot_pkglibdir], [dovecot_docdir]) - AX_SUBST_L([DOVECOT_CFLAGS], [DOVECOT_LIBS], [DOVECOT_SSL_LIBS], [DOVECOT_SQL_LIBS]) - AX_SUBST_L([LIBDOVECOT], [LIBDOVECOT_LOGIN], [LIBDOVECOT_SQL], [LIBDOVECOT_SSL], [LIBDOVECOT_LDA], [LIBDOVECOT_STORAGE]) - AX_SUBST_L([LIBDOVECOT_DEPS], [LIBDOVECOT_LOGIN_DEPS], [LIBDOVECOT_SQL_DEPS], [LIBDOVECOT_SSL_DEPS], [LIBDOVECOT_LDA_DEPS], [LIBDOVECOT_STORAGE_DEPS]) + AX_SUBST_L([DOVECOT_CFLAGS], [DOVECOT_LIBS], [DOVECOT_SSL_LIBS], [DOVECOT_SQL_LIBS], [DOVECOT_COMPRESS_LIBS]) + AX_SUBST_L([LIBDOVECOT], [LIBDOVECOT_LOGIN], [LIBDOVECOT_SQL], [LIBDOVECOT_SSL], [LIBDOVECOT_COMPRESS], [LIBDOVECOT_LDA], [LIBDOVECOT_STORAGE]) + AX_SUBST_L([LIBDOVECOT_DEPS], [LIBDOVECOT_LOGIN_DEPS], [LIBDOVECOT_SQL_DEPS], [LIBDOVECOT_SSL_DEPS], [LIBDOVECOT_COMPRESS_DEPS], [LIBDOVECOT_LDA_DEPS], [LIBDOVECOT_STORAGE_DEPS]) AX_SUBST_L([LIBDOVECOT_INCLUDE], [LIBDOVECOT_LDA_INCLUDE], [LIBDOVECOT_SERVICE_INCLUDE], [LIBDOVECOT_STORAGE_INCLUDE], [LIBDOVECOT_LOGIN_INCLUDE], [LIBDOVECOT_CONFIG_INCLUDE], [LIBDOVECOT_IMAP_INCLUDE]) DC_PLUGIN_DEPS
--- a/src/Makefile.am Tue Jul 31 19:32:03 2012 +0300 +++ b/src/Makefile.am Wed Aug 01 18:30:40 2012 +0300 @@ -16,6 +16,7 @@ lib-test \ $(LIBDOVECOT_SUBDIRS) \ lib-imap-client \ + lib-compression \ lib-dovecot \ lib-index \ lib-storage \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-compression/Makefile.am Wed Aug 01 18:30:40 2012 +0300 @@ -0,0 +1,25 @@ +noinst_LTLIBRARIES = libcompression.la + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test + +libcompression_la_SOURCES = \ + compression.c \ + istream-zlib.c \ + istream-bzlib.c \ + ostream-zlib.c \ + ostream-bzlib.c +libcompression_la_LIBADD = \ + $(COMPRESS_LIBS) + +pkginc_libdir = $(pkgincludedir) + compression.h \ + istream-zlib.h \ + ostream-zlib.h + +pkglib_LTLIBRARIES = libdovecot-compression.la +libdovecot_compression_la_SOURCES = +libdovecot_compression_la_LIBADD = libcompression.la ../lib/liblib.la $(MODULE_LIBS) $(COMPRESS_LIBS) +libdovecot_compression_la_DEPENDENCIES = libcompression.la +libdovecot_compression_la_LDFLAGS = -export-dynamic
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-compression/compression.c Wed Aug 01 18:30:40 2012 +0300 @@ -0,0 +1,99 @@ +/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream.h" +#include "istream-zlib.h" +#include "ostream-zlib.h" +#include "compression.h" + +#ifndef HAVE_ZLIB +# define i_stream_create_gz NULL +# define o_stream_create_gz NULL +#endif +#ifndef HAVE_BZLIB +# define i_stream_create_bz2 NULL +# define o_stream_create_bz2 NULL +#endif + +static bool is_compressed_zlib(struct istream *input) +{ + const unsigned char *data; + size_t size; + + /* Peek in to the stream and see if it looks like it's compressed + (based on its header). This also means that users can try to exploit + security holes in the uncompression library by APPENDing a specially + crafted mail. So let's hope zlib is free of holes. */ + if (i_stream_read_data(input, &data, &size, 1) <= 0) + return FALSE; + i_assert(size >= 2); + + return data[0] == 31 && data[1] == 139; +} + +static bool is_compressed_bzlib(struct istream *input) +{ + const unsigned char *data; + size_t size; + + if (i_stream_read_data(input, &data, &size, 4+6 - 1) <= 0) + return FALSE; + if (data[0] != 'B' || data[1] != 'Z') + return FALSE; + if (data[2] != 'h' && data[2] != '0') + return FALSE; + if (data[3] < '1' || data[3] > '9') + return FALSE; + return memcmp(data + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0; +} + +const struct compression_handler *compression_lookup_handler(const char *name) +{ + unsigned int i; + + for (i = 0; compression_handlers[i].name != NULL; i++) { + if (strcmp(name, compression_handlers[i].name) == 0) + return &compression_handlers[i]; + } + return NULL; +} + +const struct compression_handler * +compression_detect_handler(struct istream *input) +{ + unsigned int i; + + for (i = 0; compression_handlers[i].name != NULL; i++) { + if (compression_handlers[i].is_compressed != NULL && + compression_handlers[i].is_compressed(input)) + return &compression_handlers[i]; + } + return NULL; +} + +const struct compression_handler * +compression_lookup_handler_from_ext(const char *path) +{ + unsigned int i, len, path_len = strlen(path); + + for (i = 0; compression_handlers[i].name != NULL; i++) { + if (compression_handlers[i].ext == NULL) + continue; + + len = strlen(compression_handlers[i].ext); + if (path_len > len && + strcmp(path + path_len - len, compression_handlers[i].ext) == 0) + return &compression_handlers[i]; + } + return NULL; +} + +const struct compression_handler compression_handlers[] = { + { "gz", ".gz", is_compressed_zlib, + i_stream_create_gz, o_stream_create_gz }, + { "bz2", ".bz2", is_compressed_bzlib, + i_stream_create_bz2, o_stream_create_bz2 }, + { "deflate", NULL, NULL, + i_stream_create_deflate, o_stream_create_deflate }, + { NULL, NULL, NULL, NULL, NULL } +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-compression/compression.h Wed Aug 01 18:30:40 2012 +0300 @@ -0,0 +1,24 @@ +#ifndef COMPRESSION_H +#define COMPRESSION_H + +struct compression_handler { + const char *name; + const char *ext; + bool (*is_compressed)(struct istream *input); + struct istream *(*create_istream)(struct istream *input, + bool log_errors); + struct ostream *(*create_ostream)(struct ostream *output, int level); +}; + +extern const struct compression_handler compression_handlers[]; + +/* Lookup handler by its name (gz, bz2) */ +const struct compression_handler *compression_lookup_handler(const char *name); +/* Detect handler by looking at the first few bytes of the input stream. */ +const struct compression_handler * +compression_detect_handler(struct istream *input); +/* Lookup handler based on filename extension in the path */ +const struct compression_handler * +compression_lookup_handler_from_ext(const char *path); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-compression/istream-bzlib.c Wed Aug 01 18:30:40 2012 +0300 @@ -0,0 +1,345 @@ +/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +#ifdef HAVE_BZLIB + +#include "istream-private.h" +#include "istream-zlib.h" +#include <bzlib.h> + +#define CHUNK_SIZE (1024*64) + +struct bzlib_istream { + struct istream_private istream; + + bz_stream zs; + uoff_t eof_offset, stream_size; + size_t prev_size, high_pos; + struct stat last_parent_statbuf; + + unsigned int log_errors:1; + unsigned int marked:1; + unsigned int zs_closed:1; +}; + +static void i_stream_bzlib_close(struct iostream_private *stream) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *)stream; + + if (!zstream->zs_closed) { + (void)BZ2_bzDecompressEnd(&zstream->zs); + zstream->zs_closed = TRUE; + } +} + +static void bzlib_read_error(struct bzlib_istream *zstream, const char *error) +{ + i_error("bzlib.read(%s): %s at %"PRIuUOFF_T, + i_stream_get_name(&zstream->istream.istream), error, + zstream->istream.abs_start_offset + + zstream->istream.istream.v_offset); +} + +static ssize_t i_stream_bzlib_read(struct istream_private *stream) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *)stream; + const unsigned char *data; + uoff_t high_offset; + size_t size; + int ret; + + high_offset = stream->istream.v_offset + (stream->pos - stream->skip); + if (zstream->eof_offset == high_offset) { + i_assert(zstream->high_pos == 0 || + zstream->high_pos == stream->pos); + stream->istream.eof = TRUE; + return -1; + } + + if (stream->pos < zstream->high_pos) { + /* we're here because we seeked back within the read buffer. */ + ret = zstream->high_pos - stream->pos; + stream->pos = zstream->high_pos; + zstream->high_pos = 0; + + if (zstream->eof_offset != (uoff_t)-1) { + high_offset = stream->istream.v_offset + + (stream->pos - stream->skip); + i_assert(zstream->eof_offset == high_offset); + stream->istream.eof = TRUE; + } + return ret; + } + zstream->high_pos = 0; + + if (stream->pos + CHUNK_SIZE > stream->buffer_size) { + /* try to keep at least CHUNK_SIZE available */ + if (!zstream->marked && stream->skip > 0) { + /* don't try to keep anything cached if we don't + have a seek mark. */ + i_stream_compress(stream); + } + if (stream->max_buffer_size == 0 || + stream->buffer_size < stream->max_buffer_size) + i_stream_grow_buffer(stream, CHUNK_SIZE); + + if (stream->pos == stream->buffer_size) { + if (stream->skip > 0) { + /* lose our buffer cache */ + i_stream_compress(stream); + } + + if (stream->pos == stream->buffer_size) + return -2; /* buffer full */ + } + } + + if (zstream->zs.avail_in == 0) { + /* need to read more data. try to read a full CHUNK_SIZE */ + i_stream_skip(stream->parent, zstream->prev_size); + if (i_stream_read_data(stream->parent, &data, &size, + CHUNK_SIZE-1) == -1 && size == 0) { + if (stream->parent->stream_errno != 0) { + stream->istream.stream_errno = + stream->parent->stream_errno; + } else { + i_assert(stream->parent->eof); + if (zstream->log_errors) { + bzlib_read_error(zstream, + "unexpected EOF"); + } + stream->istream.stream_errno = EINVAL; + } + return -1; + } + zstream->prev_size = size; + if (size == 0) { + /* no more input */ + i_assert(!stream->istream.blocking); + return 0; + } + + zstream->zs.next_in = (char *)data; + zstream->zs.avail_in = size; + } + + size = stream->buffer_size - stream->pos; + zstream->zs.next_out = (char *)stream->w_buffer + stream->pos; + zstream->zs.avail_out = size; + ret = BZ2_bzDecompress(&zstream->zs); + + size -= zstream->zs.avail_out; + stream->pos += size; + + switch (ret) { + case BZ_OK: + break; + case BZ_PARAM_ERROR: + i_unreached(); + case BZ_DATA_ERROR: + if (zstream->log_errors) + bzlib_read_error(zstream, "corrupted data"); + stream->istream.stream_errno = EINVAL; + return -1; + case BZ_DATA_ERROR_MAGIC: + if (zstream->log_errors) { + bzlib_read_error(zstream, + "wrong magic in header (not bz2 file?)"); + } + stream->istream.stream_errno = EINVAL; + return -1; + case BZ_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, "bzlib.read(%s): Out of memory", + i_stream_get_name(&stream->istream)); + case BZ_STREAM_END: + zstream->eof_offset = stream->istream.v_offset + + (stream->pos - stream->skip); + zstream->stream_size = zstream->eof_offset; + if (size == 0) { + stream->istream.eof = TRUE; + return -1; + } + break; + default: + i_fatal("BZ2_bzDecompress() failed with %d", ret); + } + if (size == 0) { + /* read more input */ + return i_stream_bzlib_read(stream); + } + return size; +} + +static void i_stream_bzlib_init(struct bzlib_istream *zstream) +{ + int ret; + + ret = BZ2_bzDecompressInit(&zstream->zs, 0, 0); + switch (ret) { + case BZ_OK: + break; + case BZ_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, "bzlib: Out of memory"); + case BZ_CONFIG_ERROR: + i_fatal("Wrong bzlib library version (broken compilation)"); + case BZ_PARAM_ERROR: + i_fatal("bzlib: Invalid parameters"); + default: + i_fatal("BZ2_bzDecompressInit() failed with %d", ret); + } +} + +static void i_stream_bzlib_reset(struct bzlib_istream *zstream) +{ + struct istream_private *stream = &zstream->istream; + + i_stream_seek(stream->parent, stream->parent_start_offset); + zstream->eof_offset = (uoff_t)-1; + zstream->zs.next_in = NULL; + zstream->zs.avail_in = 0; + + stream->parent_expected_offset = stream->parent_start_offset; + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + zstream->high_pos = 0; + zstream->prev_size = 0; + + (void)BZ2_bzDecompressEnd(&zstream->zs); + i_stream_bzlib_init(zstream); +} + +static void +i_stream_bzlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *) stream; + uoff_t start_offset = stream->istream.v_offset - stream->skip; + + if (v_offset < start_offset) { + /* have to seek backwards */ + i_stream_bzlib_reset(zstream); + start_offset = 0; + } else if (zstream->high_pos != 0) { + stream->pos = zstream->high_pos; + zstream->high_pos = 0; + } + + if (v_offset <= start_offset + stream->pos) { + /* seeking backwards within what's already cached */ + stream->skip = v_offset - start_offset; + stream->istream.v_offset = v_offset; + zstream->high_pos = stream->pos; + stream->pos = stream->skip; + } else { + /* read and cache forward */ + do { + size_t avail = stream->pos - stream->skip; + + if (stream->istream.v_offset + avail >= v_offset) { + i_stream_skip(&stream->istream, + v_offset - + stream->istream.v_offset); + break; + } + + i_stream_skip(&stream->istream, avail); + } while (i_stream_read(&stream->istream) >= 0); + + if (stream->istream.v_offset != v_offset) { + /* some failure, we've broken it */ + if (stream->istream.stream_errno != 0) { + i_error("bzlib_istream.seek(%s) failed: %s", + i_stream_get_name(&stream->istream), + strerror(stream->istream.stream_errno)); + i_stream_close(&stream->istream); + } else { + /* unexpected EOF. allow it since we may just + want to check if there's anything.. */ + i_assert(stream->istream.eof); + } + } + } + + if (mark) + zstream->marked = TRUE; +} + +static const struct stat * +i_stream_bzlib_stat(struct istream_private *stream, bool exact) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *) stream; + const struct stat *st; + size_t size; + + st = i_stream_stat(stream->parent, exact); + if (st == NULL) + return NULL; + + /* when exact=FALSE always return the parent stat's size, even if we + know the exact value. this is necessary because otherwise e.g. mbox + code can see two different values and think that a compressed mbox + file keeps changing. */ + if (!exact) + return st; + + stream->statbuf = *st; + if (zstream->stream_size == (uoff_t)-1) { + uoff_t old_offset = stream->istream.v_offset; + + do { + size = i_stream_get_data_size(&stream->istream); + i_stream_skip(&stream->istream, size); + } while (i_stream_read(&stream->istream) > 0); + + i_stream_seek(&stream->istream, old_offset); + if (zstream->stream_size == (uoff_t)-1) + return NULL; + } + stream->statbuf.st_size = zstream->stream_size; + return &stream->statbuf; +} + +static void i_stream_bzlib_sync(struct istream_private *stream) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *) stream; + const struct stat *st; + + st = i_stream_stat(stream->parent, FALSE); + if (st != NULL) { + if (memcmp(&zstream->last_parent_statbuf, + st, sizeof(*st)) == 0) { + /* a compressed file doesn't change unexpectedly, + don't clear our caches unnecessarily */ + return; + } + zstream->last_parent_statbuf = *st; + } + i_stream_bzlib_reset(zstream); +} + +struct istream *i_stream_create_bz2(struct istream *input, bool log_errors) +{ + struct bzlib_istream *zstream; + + zstream = i_new(struct bzlib_istream, 1); + zstream->eof_offset = (uoff_t)-1; + zstream->stream_size = (uoff_t)-1; + zstream->log_errors = log_errors; + + i_stream_bzlib_init(zstream); + + zstream->istream.iostream.close = i_stream_bzlib_close; + zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + zstream->istream.read = i_stream_bzlib_read; + zstream->istream.seek = i_stream_bzlib_seek; + zstream->istream.stat = i_stream_bzlib_stat; + zstream->istream.sync = i_stream_bzlib_sync; + + zstream->istream.istream.readable_fd = FALSE; + zstream->istream.istream.blocking = input->blocking; + zstream->istream.istream.seekable = input->seekable; + + return i_stream_create(&zstream->istream, input, + i_stream_get_fd(input)); +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-compression/istream-zlib.c Wed Aug 01 18:30:40 2012 +0300 @@ -0,0 +1,516 @@ +/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +#ifdef HAVE_ZLIB + +#include "crc32.h" +#include "istream-private.h" +#include "istream-zlib.h" +#include <zlib.h> + +#define CHUNK_SIZE (1024*64) + +#define GZ_HEADER_MIN_SIZE 10 +#define GZ_TRAILER_SIZE 8 + +#define GZ_MAGIC1 0x1f +#define GZ_MAGIC2 0x8b +#define GZ_FLAG_FHCRC 0x02 +#define GZ_FLAG_FEXTRA 0x04 +#define GZ_FLAG_FNAME 0x08 +#define GZ_FLAG_FCOMMENT 0x10 + +struct zlib_istream { + struct istream_private istream; + + z_stream zs; + uoff_t eof_offset, stream_size; + size_t prev_size, high_pos; + uint32_t crc32; + struct stat last_parent_statbuf; + + unsigned int gz:1; + unsigned int log_errors:1; + unsigned int marked:1; + unsigned int header_read:1; + unsigned int trailer_read:1; + unsigned int zs_closed:1; +}; + +static void i_stream_zlib_init(struct zlib_istream *zstream); + +static void i_stream_zlib_close(struct iostream_private *stream) +{ + struct zlib_istream *zstream = (struct zlib_istream *)stream; + + if (!zstream->zs_closed) { + (void)inflateEnd(&zstream->zs); + zstream->zs_closed = TRUE; + } +} + +static void zlib_read_error(struct zlib_istream *zstream, const char *error) +{ + i_error("zlib.read(%s): %s at %"PRIuUOFF_T, + i_stream_get_name(&zstream->istream.istream), error, + zstream->istream.abs_start_offset + + zstream->istream.istream.v_offset); +} + +static int i_stream_zlib_read_header(struct istream_private *stream) +{ + struct zlib_istream *zstream = (struct zlib_istream *)stream; + const unsigned char *data; + size_t size; + unsigned int pos, fextra_size; + int ret; + + ret = i_stream_read_data(stream->parent, &data, &size, + zstream->prev_size); + if (size == zstream->prev_size) { + if (ret == -1) { + if (zstream->log_errors) + zlib_read_error(zstream, "missing gz header"); + stream->istream.stream_errno = EINVAL; + } + return ret; + } + zstream->prev_size = size; + + if (size < GZ_HEADER_MIN_SIZE) + return 0; + pos = GZ_HEADER_MIN_SIZE; + + if (data[0] != GZ_MAGIC1 || data[1] != GZ_MAGIC2) { + /* missing gzip magic header */ + if (zstream->log_errors) { + zlib_read_error(zstream, "wrong magic in header " + "(not gz file?)"); + } + stream->istream.stream_errno = EINVAL; + return -1; + } + if ((data[3] & GZ_FLAG_FEXTRA) != 0) { + if (pos + 2 < size) + return 0; + + fextra_size = data[pos] + (data[pos+1] << 8); + pos += 2; + if (pos + fextra_size < size) + return 0; + pos += fextra_size; + } + if ((data[3] & GZ_FLAG_FNAME) != 0) { + do { + if (pos == size) + return 0; + } while (data[pos++] != '\0'); + } + if ((data[3] & GZ_FLAG_FCOMMENT) != 0) { + do { + if (pos == size) + return 0; + } while (data[pos++] != '\0'); + } + if ((data[3] & GZ_FLAG_FHCRC) != 0) { + if (pos + 2 < size) + return 0; + pos += 2; + } + i_stream_skip(stream->parent, pos); + return 1; +} + +static uint32_t data_get_uint32(const unsigned char *data) +{ + return data[0] | (data[1] << 8) | (data[2] << 16) | + ((uint32_t)data[3] << 24); +} + +static int i_stream_zlib_read_trailer(struct zlib_istream *zstream) +{ + struct istream_private *stream = &zstream->istream; + const unsigned char *data; + size_t size; + int ret; + + ret = i_stream_read_data(stream->parent, &data, &size, + GZ_TRAILER_SIZE-1); + if (size == zstream->prev_size) { + if (ret == -1) { + if (zstream->log_errors) + zlib_read_error(zstream, "missing gz trailer"); + stream->istream.stream_errno = EINVAL; + } + return ret; + } + zstream->prev_size = size; + + if (size < GZ_TRAILER_SIZE) + return 0; + + if (data_get_uint32(data) != zstream->crc32) { + if (zstream->log_errors) { + zlib_read_error(zstream, + "gz trailer has wrong CRC value"); + } + stream->istream.stream_errno = EINVAL; + return -1; + } + i_stream_skip(stream->parent, GZ_TRAILER_SIZE); + zstream->prev_size = 0; + zstream->trailer_read = TRUE; + return 1; +} + +static ssize_t i_stream_zlib_read(struct istream_private *stream) +{ + struct zlib_istream *zstream = (struct zlib_istream *)stream; + const unsigned char *data; + uoff_t high_offset; + size_t size; + int ret; + + high_offset = stream->istream.v_offset + (stream->pos - stream->skip); + if (zstream->eof_offset == high_offset) { + i_assert(zstream->high_pos == 0 || + zstream->high_pos == stream->pos); + if (!zstream->trailer_read) { + do { + ret = i_stream_zlib_read_trailer(zstream); + } while (ret == 0 && stream->istream.blocking); + if (ret <= 0) + return ret; + } + if (!zstream->gz || i_stream_is_eof(stream->parent)) { + stream->istream.eof = TRUE; + return -1; + } + /* gzip file with concatenated content */ + zstream->eof_offset = (uoff_t)-1; + zstream->stream_size = (uoff_t)-1; + zstream->header_read = FALSE; + zstream->trailer_read = FALSE; + zstream->crc32 = 0; + + (void)inflateEnd(&zstream->zs); + i_stream_zlib_init(zstream); + } + + if (!zstream->header_read) { + do { + ret = i_stream_zlib_read_header(stream); + } while (ret == 0 && stream->istream.blocking); + if (ret <= 0) + return ret; + zstream->header_read = TRUE; + zstream->prev_size = 0; + } + + if (stream->pos < zstream->high_pos) { + /* we're here because we seeked back within the read buffer. */ + ret = zstream->high_pos - stream->pos; + stream->pos = zstream->high_pos; + zstream->high_pos = 0; + if (zstream->trailer_read) { + high_offset = stream->istream.v_offset + + (stream->pos - stream->skip); + i_assert(zstream->eof_offset == high_offset); + stream->istream.eof = TRUE; + } + return ret; + } + zstream->high_pos = 0; + + if (stream->pos + CHUNK_SIZE > stream->buffer_size) { + /* try to keep at least CHUNK_SIZE available */ + if (!zstream->marked && stream->skip > 0) { + /* don't try to keep anything cached if we don't + have a seek mark. */ + i_stream_compress(stream); + } + if (stream->max_buffer_size == 0 || + stream->buffer_size < stream->max_buffer_size) + i_stream_grow_buffer(stream, CHUNK_SIZE); + + if (stream->pos == stream->buffer_size) { + if (stream->skip > 0) { + /* lose our buffer cache */ + i_stream_compress(stream); + } + + if (stream->pos == stream->buffer_size) + return -2; /* buffer full */ + } + } + + if (zstream->zs.avail_in == 0) { + /* need to read more data. try to read a full CHUNK_SIZE */ + i_stream_skip(stream->parent, zstream->prev_size); + if (i_stream_read_data(stream->parent, &data, &size, + CHUNK_SIZE-1) == -1 && size == 0) { + if (stream->parent->stream_errno != 0) { + stream->istream.stream_errno = + stream->parent->stream_errno; + } else { + i_assert(stream->parent->eof); + if (zstream->log_errors) { + zlib_read_error(zstream, + "unexpected EOF"); + } + stream->istream.stream_errno = EPIPE; + } + return -1; + } + zstream->prev_size = size; + if (size == 0) { + /* no more input */ + i_assert(!stream->istream.blocking); + return 0; + } + + zstream->zs.next_in = (void *)data; + zstream->zs.avail_in = size; + } + + size = stream->buffer_size - stream->pos; + zstream->zs.next_out = stream->w_buffer + stream->pos; + zstream->zs.avail_out = size; + ret = inflate(&zstream->zs, Z_SYNC_FLUSH); + + size -= zstream->zs.avail_out; + zstream->crc32 = crc32_data_more(zstream->crc32, + stream->w_buffer + stream->pos, size); + stream->pos += size; + + switch (ret) { + case Z_OK: + break; + case Z_NEED_DICT: + if (zstream->log_errors) + zlib_read_error(zstream, "can't read file without dict"); + stream->istream.stream_errno = EINVAL; + return -1; + case Z_DATA_ERROR: + if (zstream->log_errors) + zlib_read_error(zstream, "corrupted data"); + stream->istream.stream_errno = EINVAL; + return -1; + case Z_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, "zlib.read(%s): Out of memory", + i_stream_get_name(&stream->istream)); + case Z_STREAM_END: + zstream->eof_offset = stream->istream.v_offset + + (stream->pos - stream->skip); + zstream->stream_size = zstream->eof_offset; + i_stream_skip(stream->parent, + zstream->prev_size - zstream->zs.avail_in); + zstream->zs.avail_in = 0; + zstream->prev_size = 0; + + if (!zstream->trailer_read) { + /* try to read and verify the trailer, we might not + be called again. */ + if (i_stream_zlib_read_trailer(zstream) < 0) + return -1; + } + break; + default: + i_fatal("inflate() failed with %d", ret); + } + if (size == 0) { + /* read more input */ + return i_stream_zlib_read(stream); + } + return size; +} + +static void i_stream_zlib_init(struct zlib_istream *zstream) +{ + int ret; + + ret = inflateInit2(&zstream->zs, -15); + switch (ret) { + case Z_OK: + break; + case Z_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, "zlib: Out of memory"); + case Z_VERSION_ERROR: + i_fatal("Wrong zlib library version (broken compilation)"); + case Z_STREAM_ERROR: + i_fatal("zlib: Invalid parameters"); + default: + i_fatal("inflateInit() failed with %d", ret); + } + zstream->header_read = !zstream->gz; + zstream->trailer_read = !zstream->gz; +} + +static void i_stream_zlib_reset(struct zlib_istream *zstream) +{ + struct istream_private *stream = &zstream->istream; + + i_stream_seek(stream->parent, stream->parent_start_offset); + zstream->eof_offset = (uoff_t)-1; + zstream->crc32 = 0; + + zstream->zs.next_in = NULL; + zstream->zs.avail_in = 0; + + stream->parent_expected_offset = stream->parent_start_offset; + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + zstream->high_pos = 0; + zstream->prev_size = 0; + + (void)inflateEnd(&zstream->zs); + i_stream_zlib_init(zstream); +} + +static void +i_stream_zlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark) +{ + struct zlib_istream *zstream = (struct zlib_istream *) stream; + uoff_t start_offset = stream->istream.v_offset - stream->skip; + + if (v_offset < start_offset) { + /* have to seek backwards */ + i_stream_zlib_reset(zstream); + start_offset = 0; + } else if (zstream->high_pos != 0) { + stream->pos = zstream->high_pos; + zstream->high_pos = 0; + } + + if (v_offset <= start_offset + stream->pos) { + /* seeking backwards within what's already cached */ + stream->skip = v_offset - start_offset; + stream->istream.v_offset = v_offset; + zstream->high_pos = stream->pos; + stream->pos = stream->skip; + } else { + /* read and cache forward */ + do { + size_t avail = stream->pos - stream->skip; + + if (stream->istream.v_offset + avail >= v_offset) { + i_stream_skip(&stream->istream, + v_offset - + stream->istream.v_offset); + break; + } + + i_stream_skip(&stream->istream, avail); + } while (i_stream_read(&stream->istream) >= 0); + + if (stream->istream.v_offset != v_offset) { + /* some failure, we've broken it */ + if (stream->istream.stream_errno != 0) { + i_error("zlib_istream.seek(%s) failed: %s", + i_stream_get_name(&stream->istream), + strerror(stream->istream.stream_errno)); + i_stream_close(&stream->istream); + } else { + /* unexpected EOF. allow it since we may just + want to check if there's anything.. */ + i_assert(stream->istream.eof); + } + } + } + + if (mark) + zstream->marked = TRUE; +} + +static const struct stat * +i_stream_zlib_stat(struct istream_private *stream, bool exact) +{ + struct zlib_istream *zstream = (struct zlib_istream *) stream; + const struct stat *st; + size_t size; + + st = i_stream_stat(stream->parent, exact); + if (st == NULL) + return NULL; + + /* when exact=FALSE always return the parent stat's size, even if we + know the exact value. this is necessary because otherwise e.g. mbox + code can see two different values and think that a compressed mbox + file keeps changing. */ + if (!exact) + return st; + + stream->statbuf = *st; + if (zstream->stream_size == (uoff_t)-1) { + uoff_t old_offset = stream->istream.v_offset; + + do { + size = i_stream_get_data_size(&stream->istream); + i_stream_skip(&stream->istream, size); + } while (i_stream_read(&stream->istream) > 0); + + i_stream_seek(&stream->istream, old_offset); + if (zstream->stream_size == (uoff_t)-1) + return NULL; + } + stream->statbuf.st_size = zstream->stream_size; + return &stream->statbuf; +} + +static void i_stream_zlib_sync(struct istream_private *stream) +{ + struct zlib_istream *zstream = (struct zlib_istream *) stream; + const struct stat *st; + + st = i_stream_stat(stream->parent, FALSE); + if (st != NULL) { + if (memcmp(&zstream->last_parent_statbuf, + st, sizeof(*st)) == 0) { + /* a compressed file doesn't change unexpectedly, + don't clear our caches unnecessarily */ + return; + } + zstream->last_parent_statbuf = *st; + } + i_stream_zlib_reset(zstream); +} + +static struct istream * +i_stream_create_zlib(struct istream *input, bool gz, bool log_errors) +{ + struct zlib_istream *zstream; + + zstream = i_new(struct zlib_istream, 1); + zstream->eof_offset = (uoff_t)-1; + zstream->stream_size = (uoff_t)-1; + zstream->gz = gz; + zstream->log_errors = log_errors; + + i_stream_zlib_init(zstream); + + zstream->istream.iostream.close = i_stream_zlib_close; + zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + zstream->istream.read = i_stream_zlib_read; + zstream->istream.seek = i_stream_zlib_seek; + zstream->istream.stat = i_stream_zlib_stat; + zstream->istream.sync = i_stream_zlib_sync; + + zstream->istream.istream.readable_fd = FALSE; + zstream->istream.istream.blocking = input->blocking; + zstream->istream.istream.seekable = input->seekable; + + return i_stream_create(&zstream->istream, input, + i_stream_get_fd(input)); +} + +struct istream *i_stream_create_gz(struct istream *input, bool log_errors) +{ + return i_stream_create_zlib(input, TRUE, log_errors); +} + +struct istream *i_stream_create_deflate(struct istream *input, bool log_errors) +{ + return i_stream_create_zlib(input, FALSE, log_errors); +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-compression/istream-zlib.h Wed Aug 01 18:30:40 2012 +0300 @@ -0,0 +1,8 @@ +#ifndef ISTREAM_ZLIB_H +#define ISTREAM_ZLIB_H + +struct istream *i_stream_create_gz(struct istream *input, bool log_errors); +struct istream *i_stream_create_deflate(struct istream *input, bool log_errors); +struct istream *i_stream_create_bz2(struct istream *input, bool log_errors); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-compression/ostream-bzlib.c Wed Aug 01 18:30:40 2012 +0300 @@ -0,0 +1,222 @@ +/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +#ifdef HAVE_BZLIB + +#include "ostream-private.h" +#include "ostream-zlib.h" +#include <bzlib.h> + +#define CHUNK_SIZE (1024*64) + +struct bzlib_ostream { + struct ostream_private ostream; + bz_stream zs; + + char outbuf[CHUNK_SIZE]; + unsigned int outbuf_offset, outbuf_used; + + unsigned int flushed:1; +}; + +static void o_stream_bzlib_close(struct iostream_private *stream) +{ + struct bzlib_ostream *zstream = (struct bzlib_ostream *)stream; + + (void)o_stream_flush(&zstream->ostream.ostream); + (void)BZ2_bzCompressEnd(&zstream->zs); +} + +static int o_stream_zlib_send_outbuf(struct bzlib_ostream *zstream) +{ + ssize_t ret; + size_t size; + + if (zstream->outbuf_used == 0) + return 1; + + size = zstream->outbuf_used - zstream->outbuf_offset; + i_assert(size > 0); + ret = o_stream_send(zstream->ostream.parent, + zstream->outbuf + zstream->outbuf_offset, size); + if (ret < 0) { + o_stream_copy_error_from_parent(&zstream->ostream); + return -1; + } + if ((size_t)ret != size) { + zstream->outbuf_offset += ret; + return 0; + } + zstream->outbuf_offset = 0; + zstream->outbuf_used = 0; + return 1; +} + +static ssize_t +o_stream_bzlib_send_chunk(struct bzlib_ostream *zstream, + const void *data, size_t size) +{ + bz_stream *zs = &zstream->zs; + int ret; + + i_assert(zstream->outbuf_used == 0); + + zs->next_in = (void *)data; + zs->avail_in = size; + while (zs->avail_in > 0) { + if (zs->avail_out == 0) { + /* previous block was compressed. send it and start + compression for a new block. */ + zs->next_out = zstream->outbuf; + zs->avail_out = sizeof(zstream->outbuf); + + zstream->outbuf_used = sizeof(zstream->outbuf); + if ((ret = o_stream_zlib_send_outbuf(zstream)) < 0) + return -1; + if (ret == 0) { + /* parent stream's buffer full */ + break; + } + } + + switch (BZ2_bzCompress(zs, BZ_RUN)) { + case BZ_RUN_OK: + break; + default: + i_unreached(); + } + } + size -= zs->avail_in; + + zstream->flushed = FALSE; + return size; +} + +static int o_stream_bzlib_send_flush(struct bzlib_ostream *zstream) +{ + bz_stream *zs = &zstream->zs; + unsigned int len; + bool done = FALSE; + int ret; + + if (zs->avail_in != 0) { + i_assert(zstream->ostream.ostream.last_failed_errno != 0); + zstream->ostream.ostream.stream_errno = + zstream->ostream.ostream.last_failed_errno; + return -1; + } + + if (zstream->flushed) + return 0; + + if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) + return ret; + + i_assert(zstream->outbuf_used == 0); + do { + len = sizeof(zstream->outbuf) - zs->avail_out; + if (len != 0) { + zs->next_out = zstream->outbuf; + zs->avail_out = sizeof(zstream->outbuf); + + zstream->outbuf_used = len; + if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) + return ret; + if (done) + break; + } + + ret = BZ2_bzCompress(zs, BZ_FINISH); + switch (ret) { + case BZ_STREAM_END: + done = TRUE; + break; + case BZ_FINISH_OK: + break; + default: + i_unreached(); + } + } while (zs->avail_out != sizeof(zstream->outbuf)); + + zstream->flushed = TRUE; + return 0; +} + +static int o_stream_bzlib_flush(struct ostream_private *stream) +{ + struct bzlib_ostream *zstream = (struct bzlib_ostream *)stream; + int ret; + + if (o_stream_bzlib_send_flush(zstream) < 0) + return -1; + + ret = o_stream_flush(stream->parent); + if (ret < 0) + o_stream_copy_error_from_parent(stream); + return ret; +} + +static ssize_t +o_stream_bzlib_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct bzlib_ostream *zstream = (struct bzlib_ostream *)stream; + ssize_t ret, bytes = 0; + unsigned int i; + + if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) { + /* error / we still couldn't flush existing data to + parent stream. */ + return ret; + } + + for (i = 0; i < iov_count; i++) { + ret = o_stream_bzlib_send_chunk(zstream, iov[i].iov_base, + iov[i].iov_len); + if (ret < 0) + return -1; + bytes += ret; + if ((size_t)ret != iov[i].iov_len) + break; + } + stream->ostream.offset += bytes; + + /* avail_in!=0 check is used to detect errors. if it's non-zero here + it simply means we didn't send all the data */ + zstream->zs.avail_in = 0; + return bytes; +} + +struct ostream *o_stream_create_bz2(struct ostream *output, int level) +{ + struct bzlib_ostream *zstream; + int ret; + + i_assert(level >= 1 && level <= 9); + + zstream = i_new(struct bzlib_ostream, 1); + zstream->ostream.sendv = o_stream_bzlib_sendv; + zstream->ostream.flush = o_stream_bzlib_flush; + zstream->ostream.iostream.close = o_stream_bzlib_close; + + ret = BZ2_bzCompressInit(&zstream->zs, level, 0, 0); + switch (ret) { + case BZ_OK: + break; + case BZ_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, + "bzlib: Out of memory"); + case BZ_CONFIG_ERROR: + i_fatal("Wrong bzlib library version (broken compilation)"); + case BZ_PARAM_ERROR: + i_fatal("bzlib: Invalid parameters"); + default: + i_fatal("BZ2_bzCompressInit() failed with %d", ret); + } + + zstream->zs.next_out = zstream->outbuf; + zstream->zs.avail_out = sizeof(zstream->outbuf); + return o_stream_create(&zstream->ostream, output); +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-compression/ostream-zlib.c Wed Aug 01 18:30:40 2012 +0300 @@ -0,0 +1,325 @@ +/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +#ifdef HAVE_ZLIB + +#include "crc32.h" +#include "ostream-private.h" +#include "ostream-zlib.h" +#include <zlib.h> + +#define CHUNK_SIZE (1024*32) +#define ZLIB_OS_CODE 0x03 /* Unix */ + +struct zlib_ostream { + struct ostream_private ostream; + z_stream zs; + + unsigned char gz_header[10]; + unsigned char outbuf[CHUNK_SIZE]; + unsigned int outbuf_offset, outbuf_used; + + uint32_t crc, bytes32; + + unsigned int gz:1; + unsigned int header_sent:1; + unsigned int flushed:1; +}; + +static void o_stream_zlib_close(struct iostream_private *stream) +{ + struct zlib_ostream *zstream = (struct zlib_ostream *)stream; + + (void)o_stream_flush(&zstream->ostream.ostream); + (void)deflateEnd(&zstream->zs); +} + +static int o_stream_zlib_send_gz_header(struct zlib_ostream *zstream) +{ + ssize_t ret; + + ret = o_stream_send(zstream->ostream.parent, zstream->gz_header, + sizeof(zstream->gz_header)); + if ((size_t)ret != sizeof(zstream->gz_header)) { + o_stream_copy_error_from_parent(&zstream->ostream); + return -1; + } + zstream->header_sent = TRUE; + return 0; +} + +static int o_stream_zlib_lsb_uint32(struct ostream *output, uint32_t num) +{ + unsigned char buf[sizeof(uint32_t)]; + unsigned int i; + + for (i = 0; i < sizeof(buf); i++) { + buf[i] = num & 0xff; + num >>= 8; + } + if (o_stream_send(output, buf, sizeof(buf)) != sizeof(buf)) + return -1; + return 0; +} + +static int o_stream_zlib_send_gz_trailer(struct zlib_ostream *zstream) +{ + struct ostream *output = zstream->ostream.parent; + + if (!zstream->gz) + return 0; + + if (o_stream_zlib_lsb_uint32(output, zstream->crc) < 0 || + o_stream_zlib_lsb_uint32(output, zstream->bytes32) < 0) { + o_stream_copy_error_from_parent(&zstream->ostream); + return -1; + } + return 0; +} + +static int o_stream_zlib_send_outbuf(struct zlib_ostream *zstream) +{ + ssize_t ret; + size_t size; + + if (zstream->outbuf_used == 0) + return 1; + + size = zstream->outbuf_used - zstream->outbuf_offset; + i_assert(size > 0); + ret = o_stream_send(zstream->ostream.parent, + zstream->outbuf + zstream->outbuf_offset, size); + if (ret < 0) { + o_stream_copy_error_from_parent(&zstream->ostream); + return -1; + } + if ((size_t)ret != size) { + zstream->outbuf_offset += ret; + return 0; + } + zstream->outbuf_offset = 0; + zstream->outbuf_used = 0; + return 1; +} + +static ssize_t +o_stream_zlib_send_chunk(struct zlib_ostream *zstream, + const void *data, size_t size) +{ + z_stream *zs = &zstream->zs; + int ret, flush; + + i_assert(zstream->outbuf_used == 0); + + flush = zstream->ostream.corked || zstream->gz ? + Z_NO_FLUSH : Z_SYNC_FLUSH; + + if (!zstream->header_sent) { + if (o_stream_zlib_send_gz_header(zstream) < 0) + return -1; + } + + zs->next_in = (void *)data; + zs->avail_in = size; + while (zs->avail_in > 0) { + if (zs->avail_out == 0) { + /* previous block was compressed. send it and start + compression for a new block. */ + zs->next_out = zstream->outbuf; + zs->avail_out = sizeof(zstream->outbuf); + + zstream->outbuf_used = sizeof(zstream->outbuf); + if ((ret = o_stream_zlib_send_outbuf(zstream)) < 0) + return -1; + if (ret == 0) { + /* parent stream's buffer full */ + break; + } + } + + switch (deflate(zs, flush)) { + case Z_OK: + case Z_BUF_ERROR: + break; + default: + i_unreached(); + } + } + size -= zs->avail_in; + + zstream->crc = crc32_data_more(zstream->crc, data, size); + zstream->bytes32 += size; + zstream->flushed = flush == Z_SYNC_FLUSH && zs->avail_in == 0 && + zs->avail_out == sizeof(zstream->outbuf); + return size; +} + +static int o_stream_zlib_send_flush(struct zlib_ostream *zstream) +{ + z_stream *zs = &zstream->zs; + unsigned int len; + bool done = FALSE; + int ret; + + if (zs->avail_in != 0) { + i_assert(zstream->ostream.ostream.last_failed_errno != 0); + zstream->ostream.ostream.stream_errno = + zstream->ostream.ostream.last_failed_errno; + return -1; + } + + if (zstream->flushed) + return 0; + if (!zstream->header_sent) { + if (o_stream_zlib_send_gz_header(zstream) < 0) + return -1; + } + + if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) + return ret; + + i_assert(zstream->outbuf_used == 0); + do { + len = sizeof(zstream->outbuf) - zs->avail_out; + if (len != 0) { + zs->next_out = zstream->outbuf; + zs->avail_out = sizeof(zstream->outbuf); + + zstream->outbuf_used = len; + if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) + return ret; + if (done) + break; + } + + switch (deflate(zs, zstream->gz ? Z_FINISH : Z_SYNC_FLUSH)) { + case Z_OK: + case Z_BUF_ERROR: + break; + case Z_STREAM_END: + done = TRUE; + break; + default: + i_unreached(); + } + } while (zs->avail_out != sizeof(zstream->outbuf)); + + if (o_stream_zlib_send_gz_trailer(zstream) < 0) + return -1; + zstream->flushed = TRUE; + return 0; +} + +static int o_stream_zlib_flush(struct ostream_private *stream) +{ + struct zlib_ostream *zstream = (struct zlib_ostream *)stream; + int ret; + + if (o_stream_zlib_send_flush(zstream) < 0) + return -1; + + ret = o_stream_flush(stream->parent); + if (ret < 0) + o_stream_copy_error_from_parent(stream); + return ret; +} + +static ssize_t +o_stream_zlib_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct zlib_ostream *zstream = (struct zlib_ostream *)stream; + ssize_t ret, bytes = 0; + unsigned int i; + + if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) { + /* error / we still couldn't flush existing data to + parent stream. */ + return ret; + } + + for (i = 0; i < iov_count; i++) { + ret = o_stream_zlib_send_chunk(zstream, iov[i].iov_base, + iov[i].iov_len); + if (ret < 0) + return -1; + bytes += ret; + if ((size_t)ret != iov[i].iov_len) + break; + } + stream->ostream.offset += bytes; + + if (!zstream->ostream.corked && i == iov_count) { + if (o_stream_zlib_send_flush(zstream) < 0) + return -1; + } + /* avail_in!=0 check is used to detect errors. if it's non-zero here + it simply means we didn't send all the data */ + zstream->zs.avail_in = 0; + return bytes; +} + +static void o_stream_zlib_init_gz_header(struct zlib_ostream *zstream, + int level, int strategy) +{ + unsigned char *hdr = zstream->gz_header; + + hdr[0] = 0x1f; + hdr[1] = 0x8b; + hdr[2] = Z_DEFLATED; + hdr[8] = level == 9 ? 2 : + (strategy >= Z_HUFFMAN_ONLY || + (level != Z_DEFAULT_COMPRESSION && level < 2) ? 4 : 0); + hdr[9] = ZLIB_OS_CODE; + i_assert(sizeof(zstream->gz_header) == 10); +} + +static struct ostream * +o_stream_create_zlib(struct ostream *output, int level, bool gz) +{ + const int strategy = Z_DEFAULT_STRATEGY; + struct zlib_ostream *zstream; + int ret; + + i_assert(level >= 1 && level <= 9); + + zstream = i_new(struct zlib_ostream, 1); + zstream->ostream.sendv = o_stream_zlib_sendv; + zstream->ostream.flush = o_stream_zlib_flush; + zstream->ostream.iostream.close = o_stream_zlib_close; + zstream->crc = 0; + zstream->gz = gz; + if (!gz) + zstream->header_sent = TRUE; + + o_stream_zlib_init_gz_header(zstream, level, strategy); + ret = deflateInit2(&zstream->zs, level, Z_DEFLATED, -15, 8, strategy); + switch (ret) { + case Z_OK: + break; + case Z_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, "deflateInit(): Out of memory"); + case Z_VERSION_ERROR: + i_fatal("Wrong zlib library version (broken compilation)"); + case Z_STREAM_ERROR: + i_fatal("Invalid compression level %d", level); + default: + i_fatal("deflateInit() failed with %d", ret); + } + + zstream->zs.next_out = zstream->outbuf; + zstream->zs.avail_out = sizeof(zstream->outbuf); + return o_stream_create(&zstream->ostream, output); +} + +struct ostream *o_stream_create_gz(struct ostream *output, int level) +{ + return o_stream_create_zlib(output, level, TRUE); +} + +struct ostream *o_stream_create_deflate(struct ostream *output, int level) +{ + return o_stream_create_zlib(output, level, FALSE); +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-compression/ostream-zlib.h Wed Aug 01 18:30:40 2012 +0300 @@ -0,0 +1,8 @@ +#ifndef OSTREAM_ZLIB_H +#define OSTREAM_ZLIB_H + +struct ostream *o_stream_create_gz(struct ostream *output, int level); +struct ostream *o_stream_create_deflate(struct ostream *output, int level); +struct ostream *o_stream_create_bz2(struct ostream *output, int level); + +#endif
--- a/src/plugins/imap-zlib/Makefile.am Tue Jul 31 19:32:03 2012 +0300 +++ b/src/plugins/imap-zlib/Makefile.am Wed Aug 01 18:30:40 2012 +0300 @@ -1,11 +1,11 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ - -I$(top_srcdir)/src/imap \ - -I$(top_srcdir)/src/plugins/zlib + -I$(top_srcdir)/src/imap imap_moduledir = $(moduledir) @@ -15,10 +15,8 @@ imap_module_LTLIBRARIES = \ lib30_imap_zlib_plugin.la -if DOVECOT_PLUGIN_DEPS lib30_imap_zlib_plugin_la_LIBADD = \ - ../zlib/lib20_zlib_plugin.la -endif + ../../lib-compression/libcompression.la lib30_imap_zlib_plugin_la_SOURCES = \ imap-zlib-plugin.c
--- a/src/plugins/imap-zlib/imap-zlib-plugin.c Tue Jul 31 19:32:03 2012 +0300 +++ b/src/plugins/imap-zlib/imap-zlib-plugin.c Wed Aug 01 18:30:40 2012 +0300 @@ -6,7 +6,7 @@ #include "ostream.h" #include "module-context.h" #include "imap-commands.h" -#include "zlib-plugin.h" +#include "compression.h" #include "imap-zlib-plugin.h" #include <stdlib.h> @@ -19,7 +19,7 @@ struct zlib_client { union imap_module_context module_ctx; - const struct zlib_handler *handler; + const struct compression_handler *handler; }; const char *imap_zlib_plugin_version = DOVECOT_VERSION; @@ -65,7 +65,7 @@ { struct client *client = cmd->client; struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client); - const struct zlib_handler *handler; + const struct compression_handler *handler; const struct imap_arg *args; struct istream *old_input; struct ostream *old_output; @@ -93,7 +93,7 @@ return TRUE; } - handler = zlib_find_zlib_handler(t_str_lcase(mechanism)); + handler = compression_lookup_handler(t_str_lcase(mechanism)); if (handler == NULL || handler->create_istream == NULL) { client_send_tagline(cmd, "NO Unknown compression mechanism."); return TRUE; @@ -129,7 +129,7 @@ struct zlib_client *zclient; if (mail_user_is_plugin_loaded(client->user, imap_zlib_module) && - zlib_find_zlib_handler("deflate") != NULL) { + compression_lookup_handler("deflate") != NULL) { zclient = p_new(client->pool, struct zlib_client, 1); MODULE_CONTEXT_SET(client, imap_zlib_imap_module, zclient); @@ -156,5 +156,4 @@ imap_client_created_hook_set(next_hook_client_created); } -const char *imap_zlib_plugin_dependencies[] = { "zlib", NULL }; const char imap_zlib_plugin_binary_dependency[] = "imap";
--- a/src/plugins/zlib/Makefile.am Tue Jul 31 19:32:03 2012 +0300 +++ b/src/plugins/zlib/Makefile.am Wed Aug 01 18:30:40 2012 +0300 @@ -4,6 +4,7 @@ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ @@ -17,26 +18,13 @@ module_LTLIBRARIES = \ lib20_zlib_plugin.la -if BUILD_ZLIB -ZLIB_LIB = -lz -endif -if BUILD_BZLIB -BZLIB_LIB = -lbz2 -endif - lib20_zlib_plugin_la_LIBADD = \ - $(ZLIB_LIB) $(BZLIB_LIB) + ../../lib-compression/libcompression.la lib20_zlib_plugin_la_SOURCES = \ - istream-bzlib.c \ - istream-zlib.c \ - ostream-zlib.c \ - ostream-bzlib.c \ zlib-plugin.c noinst_HEADERS = \ - istream-zlib.h \ - ostream-zlib.h \ zlib-plugin.h doveadm_module_LTLIBRARIES = \
--- a/src/plugins/zlib/istream-bzlib.c Tue Jul 31 19:32:03 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,345 +0,0 @@ -/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ - -#include "lib.h" - -#ifdef HAVE_BZLIB - -#include "istream-private.h" -#include "istream-zlib.h" -#include <bzlib.h> - -#define CHUNK_SIZE (1024*64) - -struct bzlib_istream { - struct istream_private istream; - - bz_stream zs; - uoff_t eof_offset, stream_size; - size_t prev_size, high_pos; - struct stat last_parent_statbuf; - - unsigned int log_errors:1; - unsigned int marked:1; - unsigned int zs_closed:1; -}; - -static void i_stream_bzlib_close(struct iostream_private *stream) -{ - struct bzlib_istream *zstream = (struct bzlib_istream *)stream; - - if (!zstream->zs_closed) { - (void)BZ2_bzDecompressEnd(&zstream->zs); - zstream->zs_closed = TRUE; - } -} - -static void bzlib_read_error(struct bzlib_istream *zstream, const char *error) -{ - i_error("bzlib.read(%s): %s at %"PRIuUOFF_T, - i_stream_get_name(&zstream->istream.istream), error, - zstream->istream.abs_start_offset + - zstream->istream.istream.v_offset); -} - -static ssize_t i_stream_bzlib_read(struct istream_private *stream) -{ - struct bzlib_istream *zstream = (struct bzlib_istream *)stream; - const unsigned char *data; - uoff_t high_offset; - size_t size; - int ret; - - high_offset = stream->istream.v_offset + (stream->pos - stream->skip); - if (zstream->eof_offset == high_offset) { - i_assert(zstream->high_pos == 0 || - zstream->high_pos == stream->pos); - stream->istream.eof = TRUE; - return -1; - } - - if (stream->pos < zstream->high_pos) { - /* we're here because we seeked back within the read buffer. */ - ret = zstream->high_pos - stream->pos; - stream->pos = zstream->high_pos; - zstream->high_pos = 0; - - if (zstream->eof_offset != (uoff_t)-1) { - high_offset = stream->istream.v_offset + - (stream->pos - stream->skip); - i_assert(zstream->eof_offset == high_offset); - stream->istream.eof = TRUE; - } - return ret; - } - zstream->high_pos = 0; - - if (stream->pos + CHUNK_SIZE > stream->buffer_size) { - /* try to keep at least CHUNK_SIZE available */ - if (!zstream->marked && stream->skip > 0) { - /* don't try to keep anything cached if we don't - have a seek mark. */ - i_stream_compress(stream); - } - if (stream->max_buffer_size == 0 || - stream->buffer_size < stream->max_buffer_size) - i_stream_grow_buffer(stream, CHUNK_SIZE); - - if (stream->pos == stream->buffer_size) { - if (stream->skip > 0) { - /* lose our buffer cache */ - i_stream_compress(stream); - } - - if (stream->pos == stream->buffer_size) - return -2; /* buffer full */ - } - } - - if (zstream->zs.avail_in == 0) { - /* need to read more data. try to read a full CHUNK_SIZE */ - i_stream_skip(stream->parent, zstream->prev_size); - if (i_stream_read_data(stream->parent, &data, &size, - CHUNK_SIZE-1) == -1 && size == 0) { - if (stream->parent->stream_errno != 0) { - stream->istream.stream_errno = - stream->parent->stream_errno; - } else { - i_assert(stream->parent->eof); - if (zstream->log_errors) { - bzlib_read_error(zstream, - "unexpected EOF"); - } - stream->istream.stream_errno = EINVAL; - } - return -1; - } - zstream->prev_size = size; - if (size == 0) { - /* no more input */ - i_assert(!stream->istream.blocking); - return 0; - } - - zstream->zs.next_in = (char *)data; - zstream->zs.avail_in = size; - } - - size = stream->buffer_size - stream->pos; - zstream->zs.next_out = (char *)stream->w_buffer + stream->pos; - zstream->zs.avail_out = size; - ret = BZ2_bzDecompress(&zstream->zs); - - size -= zstream->zs.avail_out; - stream->pos += size; - - switch (ret) { - case BZ_OK: - break; - case BZ_PARAM_ERROR: - i_unreached(); - case BZ_DATA_ERROR: - if (zstream->log_errors) - bzlib_read_error(zstream, "corrupted data"); - stream->istream.stream_errno = EINVAL; - return -1; - case BZ_DATA_ERROR_MAGIC: - if (zstream->log_errors) { - bzlib_read_error(zstream, - "wrong magic in header (not bz2 file?)"); - } - stream->istream.stream_errno = EINVAL; - return -1; - case BZ_MEM_ERROR: - i_fatal_status(FATAL_OUTOFMEM, "bzlib.read(%s): Out of memory", - i_stream_get_name(&stream->istream)); - case BZ_STREAM_END: - zstream->eof_offset = stream->istream.v_offset + - (stream->pos - stream->skip); - zstream->stream_size = zstream->eof_offset; - if (size == 0) { - stream->istream.eof = TRUE; - return -1; - } - break; - default: - i_fatal("BZ2_bzDecompress() failed with %d", ret); - } - if (size == 0) { - /* read more input */ - return i_stream_bzlib_read(stream); - } - return size; -} - -static void i_stream_bzlib_init(struct bzlib_istream *zstream) -{ - int ret; - - ret = BZ2_bzDecompressInit(&zstream->zs, 0, 0); - switch (ret) { - case BZ_OK: - break; - case BZ_MEM_ERROR: - i_fatal_status(FATAL_OUTOFMEM, "bzlib: Out of memory"); - case BZ_CONFIG_ERROR: - i_fatal("Wrong bzlib library version (broken compilation)"); - case BZ_PARAM_ERROR: - i_fatal("bzlib: Invalid parameters"); - default: - i_fatal("BZ2_bzDecompressInit() failed with %d", ret); - } -} - -static void i_stream_bzlib_reset(struct bzlib_istream *zstream) -{ - struct istream_private *stream = &zstream->istream; - - i_stream_seek(stream->parent, stream->parent_start_offset); - zstream->eof_offset = (uoff_t)-1; - zstream->zs.next_in = NULL; - zstream->zs.avail_in = 0; - - stream->parent_expected_offset = stream->parent_start_offset; - stream->skip = stream->pos = 0; - stream->istream.v_offset = 0; - zstream->high_pos = 0; - zstream->prev_size = 0; - - (void)BZ2_bzDecompressEnd(&zstream->zs); - i_stream_bzlib_init(zstream); -} - -static void -i_stream_bzlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark) -{ - struct bzlib_istream *zstream = (struct bzlib_istream *) stream; - uoff_t start_offset = stream->istream.v_offset - stream->skip; - - if (v_offset < start_offset) { - /* have to seek backwards */ - i_stream_bzlib_reset(zstream); - start_offset = 0; - } else if (zstream->high_pos != 0) { - stream->pos = zstream->high_pos; - zstream->high_pos = 0; - } - - if (v_offset <= start_offset + stream->pos) { - /* seeking backwards within what's already cached */ - stream->skip = v_offset - start_offset; - stream->istream.v_offset = v_offset; - zstream->high_pos = stream->pos; - stream->pos = stream->skip; - } else { - /* read and cache forward */ - do { - size_t avail = stream->pos - stream->skip; - - if (stream->istream.v_offset + avail >= v_offset) { - i_stream_skip(&stream->istream, - v_offset - - stream->istream.v_offset); - break; - } - - i_stream_skip(&stream->istream, avail); - } while (i_stream_read(&stream->istream) >= 0); - - if (stream->istream.v_offset != v_offset) { - /* some failure, we've broken it */ - if (stream->istream.stream_errno != 0) { - i_error("bzlib_istream.seek(%s) failed: %s", - i_stream_get_name(&stream->istream), - strerror(stream->istream.stream_errno)); - i_stream_close(&stream->istream); - } else { - /* unexpected EOF. allow it since we may just - want to check if there's anything.. */ - i_assert(stream->istream.eof); - } - } - } - - if (mark) - zstream->marked = TRUE; -} - -static const struct stat * -i_stream_bzlib_stat(struct istream_private *stream, bool exact) -{ - struct bzlib_istream *zstream = (struct bzlib_istream *) stream; - const struct stat *st; - size_t size; - - st = i_stream_stat(stream->parent, exact); - if (st == NULL) - return NULL; - - /* when exact=FALSE always return the parent stat's size, even if we - know the exact value. this is necessary because otherwise e.g. mbox - code can see two different values and think that a compressed mbox - file keeps changing. */ - if (!exact) - return st; - - stream->statbuf = *st; - if (zstream->stream_size == (uoff_t)-1) { - uoff_t old_offset = stream->istream.v_offset; - - do { - size = i_stream_get_data_size(&stream->istream); - i_stream_skip(&stream->istream, size); - } while (i_stream_read(&stream->istream) > 0); - - i_stream_seek(&stream->istream, old_offset); - if (zstream->stream_size == (uoff_t)-1) - return NULL; - } - stream->statbuf.st_size = zstream->stream_size; - return &stream->statbuf; -} - -static void i_stream_bzlib_sync(struct istream_private *stream) -{ - struct bzlib_istream *zstream = (struct bzlib_istream *) stream; - const struct stat *st; - - st = i_stream_stat(stream->parent, FALSE); - if (st != NULL) { - if (memcmp(&zstream->last_parent_statbuf, - st, sizeof(*st)) == 0) { - /* a compressed file doesn't change unexpectedly, - don't clear our caches unnecessarily */ - return; - } - zstream->last_parent_statbuf = *st; - } - i_stream_bzlib_reset(zstream); -} - -struct istream *i_stream_create_bz2(struct istream *input, bool log_errors) -{ - struct bzlib_istream *zstream; - - zstream = i_new(struct bzlib_istream, 1); - zstream->eof_offset = (uoff_t)-1; - zstream->stream_size = (uoff_t)-1; - zstream->log_errors = log_errors; - - i_stream_bzlib_init(zstream); - - zstream->istream.iostream.close = i_stream_bzlib_close; - zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; - zstream->istream.read = i_stream_bzlib_read; - zstream->istream.seek = i_stream_bzlib_seek; - zstream->istream.stat = i_stream_bzlib_stat; - zstream->istream.sync = i_stream_bzlib_sync; - - zstream->istream.istream.readable_fd = FALSE; - zstream->istream.istream.blocking = input->blocking; - zstream->istream.istream.seekable = input->seekable; - - return i_stream_create(&zstream->istream, input, - i_stream_get_fd(input)); -} -#endif
--- a/src/plugins/zlib/istream-zlib.c Tue Jul 31 19:32:03 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,516 +0,0 @@ -/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ - -#include "lib.h" - -#ifdef HAVE_ZLIB - -#include "crc32.h" -#include "istream-private.h" -#include "istream-zlib.h" -#include <zlib.h> - -#define CHUNK_SIZE (1024*64) - -#define GZ_HEADER_MIN_SIZE 10 -#define GZ_TRAILER_SIZE 8 - -#define GZ_MAGIC1 0x1f -#define GZ_MAGIC2 0x8b -#define GZ_FLAG_FHCRC 0x02 -#define GZ_FLAG_FEXTRA 0x04 -#define GZ_FLAG_FNAME 0x08 -#define GZ_FLAG_FCOMMENT 0x10 - -struct zlib_istream { - struct istream_private istream; - - z_stream zs; - uoff_t eof_offset, stream_size; - size_t prev_size, high_pos; - uint32_t crc32; - struct stat last_parent_statbuf; - - unsigned int gz:1; - unsigned int log_errors:1; - unsigned int marked:1; - unsigned int header_read:1; - unsigned int trailer_read:1; - unsigned int zs_closed:1; -}; - -static void i_stream_zlib_init(struct zlib_istream *zstream); - -static void i_stream_zlib_close(struct iostream_private *stream) -{ - struct zlib_istream *zstream = (struct zlib_istream *)stream; - - if (!zstream->zs_closed) { - (void)inflateEnd(&zstream->zs); - zstream->zs_closed = TRUE; - } -} - -static void zlib_read_error(struct zlib_istream *zstream, const char *error) -{ - i_error("zlib.read(%s): %s at %"PRIuUOFF_T, - i_stream_get_name(&zstream->istream.istream), error, - zstream->istream.abs_start_offset + - zstream->istream.istream.v_offset); -} - -static int i_stream_zlib_read_header(struct istream_private *stream) -{ - struct zlib_istream *zstream = (struct zlib_istream *)stream; - const unsigned char *data; - size_t size; - unsigned int pos, fextra_size; - int ret; - - ret = i_stream_read_data(stream->parent, &data, &size, - zstream->prev_size); - if (size == zstream->prev_size) { - if (ret == -1) { - if (zstream->log_errors) - zlib_read_error(zstream, "missing gz header"); - stream->istream.stream_errno = EINVAL; - } - return ret; - } - zstream->prev_size = size; - - if (size < GZ_HEADER_MIN_SIZE) - return 0; - pos = GZ_HEADER_MIN_SIZE; - - if (data[0] != GZ_MAGIC1 || data[1] != GZ_MAGIC2) { - /* missing gzip magic header */ - if (zstream->log_errors) { - zlib_read_error(zstream, "wrong magic in header " - "(not gz file?)"); - } - stream->istream.stream_errno = EINVAL; - return -1; - } - if ((data[3] & GZ_FLAG_FEXTRA) != 0) { - if (pos + 2 < size) - return 0; - - fextra_size = data[pos] + (data[pos+1] << 8); - pos += 2; - if (pos + fextra_size < size) - return 0; - pos += fextra_size; - } - if ((data[3] & GZ_FLAG_FNAME) != 0) { - do { - if (pos == size) - return 0; - } while (data[pos++] != '\0'); - } - if ((data[3] & GZ_FLAG_FCOMMENT) != 0) { - do { - if (pos == size) - return 0; - } while (data[pos++] != '\0'); - } - if ((data[3] & GZ_FLAG_FHCRC) != 0) { - if (pos + 2 < size) - return 0; - pos += 2; - } - i_stream_skip(stream->parent, pos); - return 1; -} - -static uint32_t data_get_uint32(const unsigned char *data) -{ - return data[0] | (data[1] << 8) | (data[2] << 16) | - ((uint32_t)data[3] << 24); -} - -static int i_stream_zlib_read_trailer(struct zlib_istream *zstream) -{ - struct istream_private *stream = &zstream->istream; - const unsigned char *data; - size_t size; - int ret; - - ret = i_stream_read_data(stream->parent, &data, &size, - GZ_TRAILER_SIZE-1); - if (size == zstream->prev_size) { - if (ret == -1) { - if (zstream->log_errors) - zlib_read_error(zstream, "missing gz trailer"); - stream->istream.stream_errno = EINVAL; - } - return ret; - } - zstream->prev_size = size; - - if (size < GZ_TRAILER_SIZE) - return 0; - - if (data_get_uint32(data) != zstream->crc32) { - if (zstream->log_errors) { - zlib_read_error(zstream, - "gz trailer has wrong CRC value"); - } - stream->istream.stream_errno = EINVAL; - return -1; - } - i_stream_skip(stream->parent, GZ_TRAILER_SIZE); - zstream->prev_size = 0; - zstream->trailer_read = TRUE; - return 1; -} - -static ssize_t i_stream_zlib_read(struct istream_private *stream) -{ - struct zlib_istream *zstream = (struct zlib_istream *)stream; - const unsigned char *data; - uoff_t high_offset; - size_t size; - int ret; - - high_offset = stream->istream.v_offset + (stream->pos - stream->skip); - if (zstream->eof_offset == high_offset) { - i_assert(zstream->high_pos == 0 || - zstream->high_pos == stream->pos); - if (!zstream->trailer_read) { - do { - ret = i_stream_zlib_read_trailer(zstream); - } while (ret == 0 && stream->istream.blocking); - if (ret <= 0) - return ret; - } - if (!zstream->gz || i_stream_is_eof(stream->parent)) { - stream->istream.eof = TRUE; - return -1; - } - /* gzip file with concatenated content */ - zstream->eof_offset = (uoff_t)-1; - zstream->stream_size = (uoff_t)-1; - zstream->header_read = FALSE; - zstream->trailer_read = FALSE; - zstream->crc32 = 0; - - (void)inflateEnd(&zstream->zs); - i_stream_zlib_init(zstream); - } - - if (!zstream->header_read) { - do { - ret = i_stream_zlib_read_header(stream); - } while (ret == 0 && stream->istream.blocking); - if (ret <= 0) - return ret; - zstream->header_read = TRUE; - zstream->prev_size = 0; - } - - if (stream->pos < zstream->high_pos) { - /* we're here because we seeked back within the read buffer. */ - ret = zstream->high_pos - stream->pos; - stream->pos = zstream->high_pos; - zstream->high_pos = 0; - if (zstream->trailer_read) { - high_offset = stream->istream.v_offset + - (stream->pos - stream->skip); - i_assert(zstream->eof_offset == high_offset); - stream->istream.eof = TRUE; - } - return ret; - } - zstream->high_pos = 0; - - if (stream->pos + CHUNK_SIZE > stream->buffer_size) { - /* try to keep at least CHUNK_SIZE available */ - if (!zstream->marked && stream->skip > 0) { - /* don't try to keep anything cached if we don't - have a seek mark. */ - i_stream_compress(stream); - } - if (stream->max_buffer_size == 0 || - stream->buffer_size < stream->max_buffer_size) - i_stream_grow_buffer(stream, CHUNK_SIZE); - - if (stream->pos == stream->buffer_size) { - if (stream->skip > 0) { - /* lose our buffer cache */ - i_stream_compress(stream); - } - - if (stream->pos == stream->buffer_size) - return -2; /* buffer full */ - } - } - - if (zstream->zs.avail_in == 0) { - /* need to read more data. try to read a full CHUNK_SIZE */ - i_stream_skip(stream->parent, zstream->prev_size); - if (i_stream_read_data(stream->parent, &data, &size, - CHUNK_SIZE-1) == -1 && size == 0) { - if (stream->parent->stream_errno != 0) { - stream->istream.stream_errno = - stream->parent->stream_errno; - } else { - i_assert(stream->parent->eof); - if (zstream->log_errors) { - zlib_read_error(zstream, - "unexpected EOF"); - } - stream->istream.stream_errno = EPIPE; - } - return -1; - } - zstream->prev_size = size; - if (size == 0) { - /* no more input */ - i_assert(!stream->istream.blocking); - return 0; - } - - zstream->zs.next_in = (void *)data; - zstream->zs.avail_in = size; - } - - size = stream->buffer_size - stream->pos; - zstream->zs.next_out = stream->w_buffer + stream->pos; - zstream->zs.avail_out = size; - ret = inflate(&zstream->zs, Z_SYNC_FLUSH); - - size -= zstream->zs.avail_out; - zstream->crc32 = crc32_data_more(zstream->crc32, - stream->w_buffer + stream->pos, size); - stream->pos += size; - - switch (ret) { - case Z_OK: - break; - case Z_NEED_DICT: - if (zstream->log_errors) - zlib_read_error(zstream, "can't read file without dict"); - stream->istream.stream_errno = EINVAL; - return -1; - case Z_DATA_ERROR: - if (zstream->log_errors) - zlib_read_error(zstream, "corrupted data"); - stream->istream.stream_errno = EINVAL; - return -1; - case Z_MEM_ERROR: - i_fatal_status(FATAL_OUTOFMEM, "zlib.read(%s): Out of memory", - i_stream_get_name(&stream->istream)); - case Z_STREAM_END: - zstream->eof_offset = stream->istream.v_offset + - (stream->pos - stream->skip); - zstream->stream_size = zstream->eof_offset; - i_stream_skip(stream->parent, - zstream->prev_size - zstream->zs.avail_in); - zstream->zs.avail_in = 0; - zstream->prev_size = 0; - - if (!zstream->trailer_read) { - /* try to read and verify the trailer, we might not - be called again. */ - if (i_stream_zlib_read_trailer(zstream) < 0) - return -1; - } - break; - default: - i_fatal("inflate() failed with %d", ret); - } - if (size == 0) { - /* read more input */ - return i_stream_zlib_read(stream); - } - return size; -} - -static void i_stream_zlib_init(struct zlib_istream *zstream) -{ - int ret; - - ret = inflateInit2(&zstream->zs, -15); - switch (ret) { - case Z_OK: - break; - case Z_MEM_ERROR: - i_fatal_status(FATAL_OUTOFMEM, "zlib: Out of memory"); - case Z_VERSION_ERROR: - i_fatal("Wrong zlib library version (broken compilation)"); - case Z_STREAM_ERROR: - i_fatal("zlib: Invalid parameters"); - default: - i_fatal("inflateInit() failed with %d", ret); - } - zstream->header_read = !zstream->gz; - zstream->trailer_read = !zstream->gz; -} - -static void i_stream_zlib_reset(struct zlib_istream *zstream) -{ - struct istream_private *stream = &zstream->istream; - - i_stream_seek(stream->parent, stream->parent_start_offset); - zstream->eof_offset = (uoff_t)-1; - zstream->crc32 = 0; - - zstream->zs.next_in = NULL; - zstream->zs.avail_in = 0; - - stream->parent_expected_offset = stream->parent_start_offset; - stream->skip = stream->pos = 0; - stream->istream.v_offset = 0; - zstream->high_pos = 0; - zstream->prev_size = 0; - - (void)inflateEnd(&zstream->zs); - i_stream_zlib_init(zstream); -} - -static void -i_stream_zlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark) -{ - struct zlib_istream *zstream = (struct zlib_istream *) stream; - uoff_t start_offset = stream->istream.v_offset - stream->skip; - - if (v_offset < start_offset) { - /* have to seek backwards */ - i_stream_zlib_reset(zstream); - start_offset = 0; - } else if (zstream->high_pos != 0) { - stream->pos = zstream->high_pos; - zstream->high_pos = 0; - } - - if (v_offset <= start_offset + stream->pos) { - /* seeking backwards within what's already cached */ - stream->skip = v_offset - start_offset; - stream->istream.v_offset = v_offset; - zstream->high_pos = stream->pos; - stream->pos = stream->skip; - } else { - /* read and cache forward */ - do { - size_t avail = stream->pos - stream->skip; - - if (stream->istream.v_offset + avail >= v_offset) { - i_stream_skip(&stream->istream, - v_offset - - stream->istream.v_offset); - break; - } - - i_stream_skip(&stream->istream, avail); - } while (i_stream_read(&stream->istream) >= 0); - - if (stream->istream.v_offset != v_offset) { - /* some failure, we've broken it */ - if (stream->istream.stream_errno != 0) { - i_error("zlib_istream.seek(%s) failed: %s", - i_stream_get_name(&stream->istream), - strerror(stream->istream.stream_errno)); - i_stream_close(&stream->istream); - } else { - /* unexpected EOF. allow it since we may just - want to check if there's anything.. */ - i_assert(stream->istream.eof); - } - } - } - - if (mark) - zstream->marked = TRUE; -} - -static const struct stat * -i_stream_zlib_stat(struct istream_private *stream, bool exact) -{ - struct zlib_istream *zstream = (struct zlib_istream *) stream; - const struct stat *st; - size_t size; - - st = i_stream_stat(stream->parent, exact); - if (st == NULL) - return NULL; - - /* when exact=FALSE always return the parent stat's size, even if we - know the exact value. this is necessary because otherwise e.g. mbox - code can see two different values and think that a compressed mbox - file keeps changing. */ - if (!exact) - return st; - - stream->statbuf = *st; - if (zstream->stream_size == (uoff_t)-1) { - uoff_t old_offset = stream->istream.v_offset; - - do { - size = i_stream_get_data_size(&stream->istream); - i_stream_skip(&stream->istream, size); - } while (i_stream_read(&stream->istream) > 0); - - i_stream_seek(&stream->istream, old_offset); - if (zstream->stream_size == (uoff_t)-1) - return NULL; - } - stream->statbuf.st_size = zstream->stream_size; - return &stream->statbuf; -} - -static void i_stream_zlib_sync(struct istream_private *stream) -{ - struct zlib_istream *zstream = (struct zlib_istream *) stream; - const struct stat *st; - - st = i_stream_stat(stream->parent, FALSE); - if (st != NULL) { - if (memcmp(&zstream->last_parent_statbuf, - st, sizeof(*st)) == 0) { - /* a compressed file doesn't change unexpectedly, - don't clear our caches unnecessarily */ - return; - } - zstream->last_parent_statbuf = *st; - } - i_stream_zlib_reset(zstream); -} - -static struct istream * -i_stream_create_zlib(struct istream *input, bool gz, bool log_errors) -{ - struct zlib_istream *zstream; - - zstream = i_new(struct zlib_istream, 1); - zstream->eof_offset = (uoff_t)-1; - zstream->stream_size = (uoff_t)-1; - zstream->gz = gz; - zstream->log_errors = log_errors; - - i_stream_zlib_init(zstream); - - zstream->istream.iostream.close = i_stream_zlib_close; - zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; - zstream->istream.read = i_stream_zlib_read; - zstream->istream.seek = i_stream_zlib_seek; - zstream->istream.stat = i_stream_zlib_stat; - zstream->istream.sync = i_stream_zlib_sync; - - zstream->istream.istream.readable_fd = FALSE; - zstream->istream.istream.blocking = input->blocking; - zstream->istream.istream.seekable = input->seekable; - - return i_stream_create(&zstream->istream, input, - i_stream_get_fd(input)); -} - -struct istream *i_stream_create_gz(struct istream *input, bool log_errors) -{ - return i_stream_create_zlib(input, TRUE, log_errors); -} - -struct istream *i_stream_create_deflate(struct istream *input, bool log_errors) -{ - return i_stream_create_zlib(input, FALSE, log_errors); -} -#endif
--- a/src/plugins/zlib/istream-zlib.h Tue Jul 31 19:32:03 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -#ifndef ISTREAM_ZLIB_H -#define ISTREAM_ZLIB_H - -struct istream *i_stream_create_gz(struct istream *input, bool log_errors); -struct istream *i_stream_create_deflate(struct istream *input, bool log_errors); -struct istream *i_stream_create_bz2(struct istream *input, bool log_errors); - -#endif
--- a/src/plugins/zlib/ostream-bzlib.c Tue Jul 31 19:32:03 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,222 +0,0 @@ -/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ - -#include "lib.h" - -#ifdef HAVE_BZLIB - -#include "ostream-private.h" -#include "ostream-zlib.h" -#include <bzlib.h> - -#define CHUNK_SIZE (1024*64) - -struct bzlib_ostream { - struct ostream_private ostream; - bz_stream zs; - - char outbuf[CHUNK_SIZE]; - unsigned int outbuf_offset, outbuf_used; - - unsigned int flushed:1; -}; - -static void o_stream_bzlib_close(struct iostream_private *stream) -{ - struct bzlib_ostream *zstream = (struct bzlib_ostream *)stream; - - (void)o_stream_flush(&zstream->ostream.ostream); - (void)BZ2_bzCompressEnd(&zstream->zs); -} - -static int o_stream_zlib_send_outbuf(struct bzlib_ostream *zstream) -{ - ssize_t ret; - size_t size; - - if (zstream->outbuf_used == 0) - return 1; - - size = zstream->outbuf_used - zstream->outbuf_offset; - i_assert(size > 0); - ret = o_stream_send(zstream->ostream.parent, - zstream->outbuf + zstream->outbuf_offset, size); - if (ret < 0) { - o_stream_copy_error_from_parent(&zstream->ostream); - return -1; - } - if ((size_t)ret != size) { - zstream->outbuf_offset += ret; - return 0; - } - zstream->outbuf_offset = 0; - zstream->outbuf_used = 0; - return 1; -} - -static ssize_t -o_stream_bzlib_send_chunk(struct bzlib_ostream *zstream, - const void *data, size_t size) -{ - bz_stream *zs = &zstream->zs; - int ret; - - i_assert(zstream->outbuf_used == 0); - - zs->next_in = (void *)data; - zs->avail_in = size; - while (zs->avail_in > 0) { - if (zs->avail_out == 0) { - /* previous block was compressed. send it and start - compression for a new block. */ - zs->next_out = zstream->outbuf; - zs->avail_out = sizeof(zstream->outbuf); - - zstream->outbuf_used = sizeof(zstream->outbuf); - if ((ret = o_stream_zlib_send_outbuf(zstream)) < 0) - return -1; - if (ret == 0) { - /* parent stream's buffer full */ - break; - } - } - - switch (BZ2_bzCompress(zs, BZ_RUN)) { - case BZ_RUN_OK: - break; - default: - i_unreached(); - } - } - size -= zs->avail_in; - - zstream->flushed = FALSE; - return size; -} - -static int o_stream_bzlib_send_flush(struct bzlib_ostream *zstream) -{ - bz_stream *zs = &zstream->zs; - unsigned int len; - bool done = FALSE; - int ret; - - if (zs->avail_in != 0) { - i_assert(zstream->ostream.ostream.last_failed_errno != 0); - zstream->ostream.ostream.stream_errno = - zstream->ostream.ostream.last_failed_errno; - return -1; - } - - if (zstream->flushed) - return 0; - - if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) - return ret; - - i_assert(zstream->outbuf_used == 0); - do { - len = sizeof(zstream->outbuf) - zs->avail_out; - if (len != 0) { - zs->next_out = zstream->outbuf; - zs->avail_out = sizeof(zstream->outbuf); - - zstream->outbuf_used = len; - if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) - return ret; - if (done) - break; - } - - ret = BZ2_bzCompress(zs, BZ_FINISH); - switch (ret) { - case BZ_STREAM_END: - done = TRUE; - break; - case BZ_FINISH_OK: - break; - default: - i_unreached(); - } - } while (zs->avail_out != sizeof(zstream->outbuf)); - - zstream->flushed = TRUE; - return 0; -} - -static int o_stream_bzlib_flush(struct ostream_private *stream) -{ - struct bzlib_ostream *zstream = (struct bzlib_ostream *)stream; - int ret; - - if (o_stream_bzlib_send_flush(zstream) < 0) - return -1; - - ret = o_stream_flush(stream->parent); - if (ret < 0) - o_stream_copy_error_from_parent(stream); - return ret; -} - -static ssize_t -o_stream_bzlib_sendv(struct ostream_private *stream, - const struct const_iovec *iov, unsigned int iov_count) -{ - struct bzlib_ostream *zstream = (struct bzlib_ostream *)stream; - ssize_t ret, bytes = 0; - unsigned int i; - - if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) { - /* error / we still couldn't flush existing data to - parent stream. */ - return ret; - } - - for (i = 0; i < iov_count; i++) { - ret = o_stream_bzlib_send_chunk(zstream, iov[i].iov_base, - iov[i].iov_len); - if (ret < 0) - return -1; - bytes += ret; - if ((size_t)ret != iov[i].iov_len) - break; - } - stream->ostream.offset += bytes; - - /* avail_in!=0 check is used to detect errors. if it's non-zero here - it simply means we didn't send all the data */ - zstream->zs.avail_in = 0; - return bytes; -} - -struct ostream *o_stream_create_bz2(struct ostream *output, int level) -{ - struct bzlib_ostream *zstream; - int ret; - - i_assert(level >= 1 && level <= 9); - - zstream = i_new(struct bzlib_ostream, 1); - zstream->ostream.sendv = o_stream_bzlib_sendv; - zstream->ostream.flush = o_stream_bzlib_flush; - zstream->ostream.iostream.close = o_stream_bzlib_close; - - ret = BZ2_bzCompressInit(&zstream->zs, level, 0, 0); - switch (ret) { - case BZ_OK: - break; - case BZ_MEM_ERROR: - i_fatal_status(FATAL_OUTOFMEM, - "bzlib: Out of memory"); - case BZ_CONFIG_ERROR: - i_fatal("Wrong bzlib library version (broken compilation)"); - case BZ_PARAM_ERROR: - i_fatal("bzlib: Invalid parameters"); - default: - i_fatal("BZ2_bzCompressInit() failed with %d", ret); - } - - zstream->zs.next_out = zstream->outbuf; - zstream->zs.avail_out = sizeof(zstream->outbuf); - return o_stream_create(&zstream->ostream, output); -} -#endif
--- a/src/plugins/zlib/ostream-zlib.c Tue Jul 31 19:32:03 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,325 +0,0 @@ -/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ - -#include "lib.h" - -#ifdef HAVE_ZLIB - -#include "crc32.h" -#include "ostream-private.h" -#include "ostream-zlib.h" -#include <zlib.h> - -#define CHUNK_SIZE (1024*32) -#define ZLIB_OS_CODE 0x03 /* Unix */ - -struct zlib_ostream { - struct ostream_private ostream; - z_stream zs; - - unsigned char gz_header[10]; - unsigned char outbuf[CHUNK_SIZE]; - unsigned int outbuf_offset, outbuf_used; - - uint32_t crc, bytes32; - - unsigned int gz:1; - unsigned int header_sent:1; - unsigned int flushed:1; -}; - -static void o_stream_zlib_close(struct iostream_private *stream) -{ - struct zlib_ostream *zstream = (struct zlib_ostream *)stream; - - (void)o_stream_flush(&zstream->ostream.ostream); - (void)deflateEnd(&zstream->zs); -} - -static int o_stream_zlib_send_gz_header(struct zlib_ostream *zstream) -{ - ssize_t ret; - - ret = o_stream_send(zstream->ostream.parent, zstream->gz_header, - sizeof(zstream->gz_header)); - if ((size_t)ret != sizeof(zstream->gz_header)) { - o_stream_copy_error_from_parent(&zstream->ostream); - return -1; - } - zstream->header_sent = TRUE; - return 0; -} - -static int o_stream_zlib_lsb_uint32(struct ostream *output, uint32_t num) -{ - unsigned char buf[sizeof(uint32_t)]; - unsigned int i; - - for (i = 0; i < sizeof(buf); i++) { - buf[i] = num & 0xff; - num >>= 8; - } - if (o_stream_send(output, buf, sizeof(buf)) != sizeof(buf)) - return -1; - return 0; -} - -static int o_stream_zlib_send_gz_trailer(struct zlib_ostream *zstream) -{ - struct ostream *output = zstream->ostream.parent; - - if (!zstream->gz) - return 0; - - if (o_stream_zlib_lsb_uint32(output, zstream->crc) < 0 || - o_stream_zlib_lsb_uint32(output, zstream->bytes32) < 0) { - o_stream_copy_error_from_parent(&zstream->ostream); - return -1; - } - return 0; -} - -static int o_stream_zlib_send_outbuf(struct zlib_ostream *zstream) -{ - ssize_t ret; - size_t size; - - if (zstream->outbuf_used == 0) - return 1; - - size = zstream->outbuf_used - zstream->outbuf_offset; - i_assert(size > 0); - ret = o_stream_send(zstream->ostream.parent, - zstream->outbuf + zstream->outbuf_offset, size); - if (ret < 0) { - o_stream_copy_error_from_parent(&zstream->ostream); - return -1; - } - if ((size_t)ret != size) { - zstream->outbuf_offset += ret; - return 0; - } - zstream->outbuf_offset = 0; - zstream->outbuf_used = 0; - return 1; -} - -static ssize_t -o_stream_zlib_send_chunk(struct zlib_ostream *zstream, - const void *data, size_t size) -{ - z_stream *zs = &zstream->zs; - int ret, flush; - - i_assert(zstream->outbuf_used == 0); - - flush = zstream->ostream.corked || zstream->gz ? - Z_NO_FLUSH : Z_SYNC_FLUSH; - - if (!zstream->header_sent) { - if (o_stream_zlib_send_gz_header(zstream) < 0) - return -1; - } - - zs->next_in = (void *)data; - zs->avail_in = size; - while (zs->avail_in > 0) { - if (zs->avail_out == 0) { - /* previous block was compressed. send it and start - compression for a new block. */ - zs->next_out = zstream->outbuf; - zs->avail_out = sizeof(zstream->outbuf); - - zstream->outbuf_used = sizeof(zstream->outbuf); - if ((ret = o_stream_zlib_send_outbuf(zstream)) < 0) - return -1; - if (ret == 0) { - /* parent stream's buffer full */ - break; - } - } - - switch (deflate(zs, flush)) { - case Z_OK: - case Z_BUF_ERROR: - break; - default: - i_unreached(); - } - } - size -= zs->avail_in; - - zstream->crc = crc32_data_more(zstream->crc, data, size); - zstream->bytes32 += size; - zstream->flushed = flush == Z_SYNC_FLUSH && zs->avail_in == 0 && - zs->avail_out == sizeof(zstream->outbuf); - return size; -} - -static int o_stream_zlib_send_flush(struct zlib_ostream *zstream) -{ - z_stream *zs = &zstream->zs; - unsigned int len; - bool done = FALSE; - int ret; - - if (zs->avail_in != 0) { - i_assert(zstream->ostream.ostream.last_failed_errno != 0); - zstream->ostream.ostream.stream_errno = - zstream->ostream.ostream.last_failed_errno; - return -1; - } - - if (zstream->flushed) - return 0; - if (!zstream->header_sent) { - if (o_stream_zlib_send_gz_header(zstream) < 0) - return -1; - } - - if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) - return ret; - - i_assert(zstream->outbuf_used == 0); - do { - len = sizeof(zstream->outbuf) - zs->avail_out; - if (len != 0) { - zs->next_out = zstream->outbuf; - zs->avail_out = sizeof(zstream->outbuf); - - zstream->outbuf_used = len; - if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) - return ret; - if (done) - break; - } - - switch (deflate(zs, zstream->gz ? Z_FINISH : Z_SYNC_FLUSH)) { - case Z_OK: - case Z_BUF_ERROR: - break; - case Z_STREAM_END: - done = TRUE; - break; - default: - i_unreached(); - } - } while (zs->avail_out != sizeof(zstream->outbuf)); - - if (o_stream_zlib_send_gz_trailer(zstream) < 0) - return -1; - zstream->flushed = TRUE; - return 0; -} - -static int o_stream_zlib_flush(struct ostream_private *stream) -{ - struct zlib_ostream *zstream = (struct zlib_ostream *)stream; - int ret; - - if (o_stream_zlib_send_flush(zstream) < 0) - return -1; - - ret = o_stream_flush(stream->parent); - if (ret < 0) - o_stream_copy_error_from_parent(stream); - return ret; -} - -static ssize_t -o_stream_zlib_sendv(struct ostream_private *stream, - const struct const_iovec *iov, unsigned int iov_count) -{ - struct zlib_ostream *zstream = (struct zlib_ostream *)stream; - ssize_t ret, bytes = 0; - unsigned int i; - - if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) { - /* error / we still couldn't flush existing data to - parent stream. */ - return ret; - } - - for (i = 0; i < iov_count; i++) { - ret = o_stream_zlib_send_chunk(zstream, iov[i].iov_base, - iov[i].iov_len); - if (ret < 0) - return -1; - bytes += ret; - if ((size_t)ret != iov[i].iov_len) - break; - } - stream->ostream.offset += bytes; - - if (!zstream->ostream.corked && i == iov_count) { - if (o_stream_zlib_send_flush(zstream) < 0) - return -1; - } - /* avail_in!=0 check is used to detect errors. if it's non-zero here - it simply means we didn't send all the data */ - zstream->zs.avail_in = 0; - return bytes; -} - -static void o_stream_zlib_init_gz_header(struct zlib_ostream *zstream, - int level, int strategy) -{ - unsigned char *hdr = zstream->gz_header; - - hdr[0] = 0x1f; - hdr[1] = 0x8b; - hdr[2] = Z_DEFLATED; - hdr[8] = level == 9 ? 2 : - (strategy >= Z_HUFFMAN_ONLY || - (level != Z_DEFAULT_COMPRESSION && level < 2) ? 4 : 0); - hdr[9] = ZLIB_OS_CODE; - i_assert(sizeof(zstream->gz_header) == 10); -} - -static struct ostream * -o_stream_create_zlib(struct ostream *output, int level, bool gz) -{ - const int strategy = Z_DEFAULT_STRATEGY; - struct zlib_ostream *zstream; - int ret; - - i_assert(level >= 1 && level <= 9); - - zstream = i_new(struct zlib_ostream, 1); - zstream->ostream.sendv = o_stream_zlib_sendv; - zstream->ostream.flush = o_stream_zlib_flush; - zstream->ostream.iostream.close = o_stream_zlib_close; - zstream->crc = 0; - zstream->gz = gz; - if (!gz) - zstream->header_sent = TRUE; - - o_stream_zlib_init_gz_header(zstream, level, strategy); - ret = deflateInit2(&zstream->zs, level, Z_DEFLATED, -15, 8, strategy); - switch (ret) { - case Z_OK: - break; - case Z_MEM_ERROR: - i_fatal_status(FATAL_OUTOFMEM, "deflateInit(): Out of memory"); - case Z_VERSION_ERROR: - i_fatal("Wrong zlib library version (broken compilation)"); - case Z_STREAM_ERROR: - i_fatal("Invalid compression level %d", level); - default: - i_fatal("deflateInit() failed with %d", ret); - } - - zstream->zs.next_out = zstream->outbuf; - zstream->zs.avail_out = sizeof(zstream->outbuf); - return o_stream_create(&zstream->ostream, output); -} - -struct ostream *o_stream_create_gz(struct ostream *output, int level) -{ - return o_stream_create_zlib(output, level, TRUE); -} - -struct ostream *o_stream_create_deflate(struct ostream *output, int level) -{ - return o_stream_create_zlib(output, level, FALSE); -} -#endif
--- a/src/plugins/zlib/ostream-zlib.h Tue Jul 31 19:32:03 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -#ifndef OSTREAM_ZLIB_H -#define OSTREAM_ZLIB_H - -struct ostream *o_stream_create_gz(struct ostream *output, int level); -struct ostream *o_stream_create_deflate(struct ostream *output, int level); -struct ostream *o_stream_create_bz2(struct ostream *output, int level); - -#endif
--- a/src/plugins/zlib/zlib-plugin.c Tue Jul 31 19:32:03 2012 +0300 +++ b/src/plugins/zlib/zlib-plugin.c Wed Aug 01 18:30:40 2012 +0300 @@ -10,8 +10,7 @@ #include "maildir/maildir-storage.h" #include "index-storage.h" #include "index-mail.h" -#include "istream-zlib.h" -#include "ostream-zlib.h" +#include "compression.h" #include "zlib-plugin.h" #include <stdlib.h> @@ -26,15 +25,6 @@ #define ZLIB_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, zlib_user_module) -#ifndef HAVE_ZLIB -# define i_stream_create_gz NULL -# define o_stream_create_gz NULL -#endif -#ifndef HAVE_BZLIB -# define i_stream_create_bz2 NULL -# define o_stream_create_bz2 NULL -#endif - #define MAX_INBUF_SIZE (1024*1024) struct zlib_transaction_context { @@ -46,7 +36,7 @@ struct zlib_user { union mail_user_module_context module_ctx; - const struct zlib_handler *save_handler; + const struct compression_handler *save_handler; unsigned int save_level; }; @@ -58,84 +48,13 @@ &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(zlib_mail_module, &mail_module_register); -static bool is_compressed_zlib(struct istream *input) -{ - const unsigned char *data; - size_t size; - - /* Peek in to the stream and see if it looks like it's compressed - (based on its header). This also means that users can try to exploit - security holes in the uncompression library by APPENDing a specially - crafted mail. So let's hope zlib is free of holes. */ - if (i_stream_read_data(input, &data, &size, 1) <= 0) - return FALSE; - i_assert(size >= 2); - - return data[0] == 31 && data[1] == 139; -} - -static bool is_compressed_bzlib(struct istream *input) -{ - const unsigned char *data; - size_t size; - - if (i_stream_read_data(input, &data, &size, 4+6 - 1) <= 0) - return FALSE; - if (data[0] != 'B' || data[1] != 'Z') - return FALSE; - if (data[2] != 'h' && data[2] != '0') - return FALSE; - if (data[3] < '1' || data[3] > '9') - return FALSE; - return memcmp(data + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0; -} - -const struct zlib_handler *zlib_find_zlib_handler(const char *name) -{ - unsigned int i; - - for (i = 0; zlib_handlers[i].name != NULL; i++) { - if (strcmp(name, zlib_handlers[i].name) == 0) - return &zlib_handlers[i]; - } - return NULL; -} - -static const struct zlib_handler *zlib_get_zlib_handler(struct istream *input) -{ - unsigned int i; - - for (i = 0; zlib_handlers[i].name != NULL; i++) { - if (zlib_handlers[i].is_compressed != NULL && - zlib_handlers[i].is_compressed(input)) - return &zlib_handlers[i]; - } - return NULL; -} - -static const struct zlib_handler *zlib_get_zlib_handler_ext(const char *name) -{ - unsigned int i, len, name_len = strlen(name); - - for (i = 0; zlib_handlers[i].name != NULL; i++) { - if (zlib_handlers[i].ext == NULL) - continue; - - len = strlen(zlib_handlers[i].ext); - if (name_len > len && - strcmp(name + name_len - len, zlib_handlers[i].ext) == 0) - return &zlib_handlers[i]; - } - return NULL; -} - static int zlib_istream_opened(struct mail *_mail, struct istream **stream) { struct zlib_user *zuser = ZLIB_USER_CONTEXT(_mail->box->storage->user); struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *zmail = ZLIB_MAIL_CONTEXT(mail); struct istream *input; - const struct zlib_handler *handler; + const struct compression_handler *handler; /* don't uncompress input when we are reading a mail that we're just in the middle of saving, and we didn't do the compression ourself. @@ -144,7 +63,7 @@ if (_mail->saving && zuser->save_handler == NULL) return zmail->super.istream_opened(_mail, stream); - handler = zlib_get_zlib_handler(*stream); + handler = compression_detect_handler(*stream); if (handler != NULL) { if (handler->create_istream == NULL) { mail_storage_set_critical(_mail->box->storage, @@ -253,7 +172,7 @@ if (mail_get_stream(ctx->dest_mail, NULL, NULL, &input) < 0) return -1; - if (zlib_get_zlib_handler(input) != NULL) { + if (compression_detect_handler(input) != NULL) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Saving mails compressed by client isn't supported"); return -1; @@ -299,12 +218,12 @@ static int zlib_mailbox_open_input(struct mailbox *box) { - const struct zlib_handler *handler; + const struct compression_handler *handler; struct istream *input; struct stat st; int fd; - handler = zlib_get_zlib_handler_ext(box->name); + handler = compression_lookup_handler_from_ext(box->name); if (handler == NULL || handler->create_istream == NULL) return 0; @@ -373,7 +292,7 @@ name = mail_user_plugin_getenv(user, "zlib_save"); if (name != NULL && *name != '\0') { - zuser->save_handler = zlib_find_zlib_handler(name); + zuser->save_handler = compression_lookup_handler(name); if (zuser->save_handler == NULL) i_error("zlib_save: Unknown handler: %s", name); } @@ -405,13 +324,3 @@ { mail_storage_hooks_remove(&zlib_mail_storage_hooks); } - -const struct zlib_handler zlib_handlers[] = { - { "gz", ".gz", is_compressed_zlib, - i_stream_create_gz, o_stream_create_gz }, - { "bz2", ".bz2", is_compressed_bzlib, - i_stream_create_bz2, o_stream_create_bz2 }, - { "deflate", NULL, NULL, - i_stream_create_deflate, o_stream_create_deflate }, - { NULL, NULL, NULL, NULL, NULL } -};
--- a/src/plugins/zlib/zlib-plugin.h Tue Jul 31 19:32:03 2012 +0300 +++ b/src/plugins/zlib/zlib-plugin.h Wed Aug 01 18:30:40 2012 +0300 @@ -1,19 +1,6 @@ #ifndef ZLIB_PLUGIN_H #define ZLIB_PLUGIN_H -struct zlib_handler { - const char *name; - const char *ext; - bool (*is_compressed)(struct istream *input); - struct istream *(*create_istream)(struct istream *input, - bool log_errors); - struct ostream *(*create_ostream)(struct ostream *output, int level); -}; - -extern const struct zlib_handler zlib_handlers[]; - -const struct zlib_handler *zlib_find_zlib_handler(const char *name); - void zlib_plugin_init(struct module *module); void zlib_plugin_deinit(void);