-/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
-#include "istream-internal.h"
-#include "istream-zlib.h"
#ifdef HAVE_BZLIB
-#include <stdio.h>
+
+#include "istream-internal.h"
+#include "istream-zlib.h"
#include <bzlib.h>
-#define BZLIB_INCLUDE
+#define CHUNK_SIZE (1024*64)
+
+struct bzlib_istream {
+ struct istream_private istream;
+
+ bz_stream zs;
+ uoff_t eof_offset;
+ size_t prev_size;
+
+ 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 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) {
+ stream->istream.eof = TRUE;
+ return -1;
+ }
+
+ 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 {
+ /* unexpected eof */
+ i_assert(stream->parent->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_in;
+ stream->pos += size;
+
+ switch (ret) {
+ case BZ_OK:
+ break;
+ case BZ_PARAM_ERROR:
+ i_unreached();
+ case BZ_DATA_ERROR:
+ case BZ_DATA_ERROR_MAGIC:
+ stream->istream.stream_errno = EINVAL;
+ return -1;
+ case BZ_MEM_ERROR:
+ i_fatal_status(FATAL_OUTOFMEM, "bzlib: Out of memory");
+ case BZ_STREAM_END:
+ zstream->eof_offset = stream->istream.v_offset + stream->pos;
+ 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, 0);
+ zstream->eof_offset = (uoff_t)-1;
+ zstream->zs.next_in = NULL;
+ zstream->zs.avail_in = 0;
+
+ stream->skip = stream->pos = 0;
+ stream->istream.v_offset = 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;
+ }
+
+ 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;
+ } 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_bzlib_read(stream) >= 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() failed: %s",
+ 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) {
+ i_stream_compress(stream);
+ 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;
+
+ if (zstream->eof_offset == (uoff_t)-1 && !exact)
+ return st;
+
+ stream->statbuf = *st;
+ if (zstream->eof_offset == (uoff_t)-1) {
+ uoff_t old_offset = stream->istream.v_offset;
+
+ do {
+ (void)i_stream_get_data(&stream->istream, &size);
+ i_stream_skip(&stream->istream, size);
+ } while (i_stream_bzlib_read(stream) > 0);
+
+ i_stream_seek(&stream->istream, old_offset);
+ if (zstream->eof_offset == (uoff_t)-1)
+ return NULL;
+ }
+ stream->statbuf.st_size = zstream->eof_offset;
+ return &stream->statbuf;
+}
+
+static void i_stream_bzlib_sync(struct istream_private *stream)
+{
+ struct bzlib_istream *zstream = (struct bzlib_istream *) stream;
+
+ i_stream_bzlib_reset(zstream);
+}
+
+struct istream *i_stream_create_bz2(struct istream *input)
+{
+ struct bzlib_istream *zstream;
+
+ zstream = i_new(struct bzlib_istream, 1);
+ zstream->eof_offset = (uoff_t)-1;
+
+ 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;
-#define gzFile BZFILE
-#define gzdopen BZ2_bzdopen
-#define gzclose BZ2_bzclose
-#define gzread BZ2_bzread
-#define gzseek BZ2_bzseek
-#define gzerror BZ2_bzerror
-#define Z_ERRNO BZ_IO_ERROR
+ zstream->istream.istream.readable_fd = FALSE;
+ zstream->istream.istream.blocking = input->blocking;
+ zstream->istream.istream.seekable = input->seekable;
-#define i_stream_create_zlib i_stream_create_bzlib
-#include "istream-zlib.c"
+ return i_stream_create(&zstream->istream, input,
+ i_stream_get_fd(input));
+}
#endif
-/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
-
-#ifdef BZLIB_INCLUDE
-# define BUILD_SOURCE
-#else
-# include "config.h"
-# undef HAVE_CONFIG_H
-# if _FILE_OFFSET_BITS == 64
-# define _LARGEFILE64_SOURCE
-# endif
-# include "lib.h"
-# include "istream-internal.h"
-# include "istream-zlib.h"
-# include <zlib.h>
-
-# ifdef HAVE_ZLIB
-# define BUILD_SOURCE
-# define HAVE_GZSEEK
-# endif
-#endif
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+
+#ifdef HAVE_ZLIB
-#ifdef BUILD_SOURCE
-/* Default maximum buffer size. Seeking backwards is very expensive, so keep
- this pretty large */
-#define DEFAULT_MAX_BUFFER_SIZE (1024*1024)
+#include "crc32.h"
+#include "istream-internal.h"
+#include "istream-zlib.h"
+#include <zlib.h>
-#include <unistd.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;
- int fd;
- gzFile *file;
- uoff_t cached_size;
- uoff_t seek_offset;
+ z_stream zs;
+ uoff_t eof_offset;
+ size_t prev_size;
+ uint32_t crc32;
+ unsigned int gz: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_close(struct iostream_private *stream)
{
struct zlib_istream *zstream = (struct zlib_istream *)stream;
- if (zstream->file != NULL) {
- gzclose(zstream->file);
- zstream->file = NULL;
+ if (!zstream->zs_closed) {
+ (void)inflateEnd(&zstream->zs);
+ zstream->zs_closed = TRUE;
+ }
+}
+
+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)
+ 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 */
+ 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)
+ 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) {
+ stream->istream.stream_errno = EINVAL;
+ return -1;
+ }
+ i_stream_skip(stream->parent, GZ_TRAILER_SIZE);
+ 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;
- const char *errstr;
- int ret, errnum;
+ int ret;
+
+ high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
+ if (zstream->eof_offset == high_offset) {
+ if (!zstream->trailer_read) {
+ do {
+ ret = i_stream_zlib_read_trailer(zstream);
+ } while (ret == 0 && stream->istream.blocking);
+ if (ret <= 0)
+ return ret;
+ }
+ stream->istream.eof = TRUE;
+ return -1;
+ }
- if (stream->pos == stream->buffer_size) {
+ 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 + 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);
- } else if (stream->max_buffer_size == 0 ||
- stream->buffer_size < stream->max_buffer_size) {
- /* buffer is full - grow it */
- i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
}
+ 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) {
}
}
- size = stream->buffer_size - stream->pos;
-
- ret = -1;
+ 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 {
+ /* unexpected eof */
+ i_assert(stream->parent->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;
+ }
- i_assert(zstream->seek_offset == stream->istream.v_offset +
- (stream->pos - stream->skip));
- do {
- ret = gzread(zstream->file, stream->w_buffer + stream->pos,
- size);
- } while (ret < 0 && errno == EINTR && stream->istream.blocking);
+ zstream->zs.next_in = (void *)data;
+ zstream->zs.avail_in = size;
+ }
- if (ret == 0) {
- /* EOF */
- stream->istream.eof = TRUE;
+ 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:
+ case Z_DATA_ERROR:
+ stream->istream.stream_errno = EINVAL;
return -1;
+ case Z_MEM_ERROR:
+ i_fatal_status(FATAL_OUTOFMEM, "zlib: Out of memory");
+ case Z_STREAM_END:
+ zstream->eof_offset = stream->istream.v_offset + stream->pos;
+ 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;
+}
- if (ret < 0) {
- errstr = gzerror(zstream->file, &errnum);
- if (errnum != Z_ERRNO) {
- i_error("gzread() failed: %s", errstr);
- stream->istream.stream_errno = EINVAL;
- return -1;
- }
- if (errno == EAGAIN) {
- i_assert(!stream->istream.blocking);
- ret = 0;
- } else {
- i_assert(errno != 0);
- stream->istream.stream_errno = errno;
- return -1;
- }
+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;
- zstream->seek_offset += ret;
- stream->pos += ret;
- i_assert(ret > 0);
- return ret;
+ i_stream_seek(stream->parent, 0);
+ zstream->eof_offset = (uoff_t)-1;
+ zstream->crc32 = 0;
+
+ zstream->zs.next_in = NULL;
+ zstream->zs.avail_in = 0;
+
+ stream->skip = stream->pos = 0;
+ stream->istream.v_offset = 0;
+
+ (void)inflateEnd(&zstream->zs);
+ i_stream_zlib_init(zstream);
}
static void
struct zlib_istream *zstream = (struct zlib_istream *) stream;
uoff_t start_offset = stream->istream.v_offset - stream->skip;
-#ifndef HAVE_GZSEEK
- if (v_offset < start_offset) {
- /* need to reopen, but since closing the file closes the
- file descriptor we'll have to duplicate it first. */
- int fd = dup(zstream->fd);
- if (fd == -1) {
- stream->istream.stream_errno = errno;
- i_error("zlib istream: dup() failed: %m");
- i_stream_close(&stream->istream);
- return;
- }
- gzclose(zstream->file);
- zstream->fd = fd;
- stream->fd = fd;
- zstream->file = gzdopen(zstream->fd, "r");
- }
-#else
if (v_offset < start_offset) {
/* have to seek backwards */
- gzseek(zstream->file, v_offset, SEEK_SET);
- zstream->seek_offset = v_offset;
+ i_stream_zlib_reset(zstream);
+ start_offset = 0;
+ }
- stream->skip = stream->pos = 0;
- stream->istream.v_offset = v_offset;
- } else
-#endif
if (v_offset <= start_offset + stream->pos) {
/* seeking backwards within what's already cached */
stream->skip = v_offset - start_offset;
/* 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 -
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;
- if (fstat(zstream->fd, &stream->statbuf) < 0) {
- i_error("zlib_istream.fstat() failed: %m");
+ st = i_stream_stat(stream->parent, exact);
+ if (st == NULL)
return NULL;
- }
- if (!exact)
- return &stream->statbuf;
+ if (zstream->eof_offset == (uoff_t)-1 && !exact)
+ return st;
- if (zstream->cached_size == (uoff_t)-1) {
+ stream->statbuf = *st;
+ if (zstream->eof_offset == (uoff_t)-1) {
uoff_t old_offset = stream->istream.v_offset;
+
do {
(void)i_stream_get_data(&stream->istream, &size);
i_stream_skip(&stream->istream, size);
} while (i_stream_zlib_read(stream) > 0);
- zstream->cached_size = stream->istream.v_offset;
i_stream_seek(&stream->istream, old_offset);
+ if (zstream->eof_offset == (uoff_t)-1)
+ return NULL;
}
- stream->statbuf.st_size = zstream->cached_size;
+ stream->statbuf.st_size = zstream->eof_offset;
return &stream->statbuf;
}
{
struct zlib_istream *zstream = (struct zlib_istream *) stream;
- zstream->cached_size = (uoff_t)-1;
+ i_stream_zlib_reset(zstream);
}
-struct istream *i_stream_create_zlib(int fd)
+static struct istream *i_stream_create_zlib(struct istream *input, bool gz)
{
struct zlib_istream *zstream;
- struct stat st;
zstream = i_new(struct zlib_istream, 1);
- zstream->fd = fd;
- zstream->file = gzdopen(fd, "r");
- zstream->cached_size = (uoff_t)-1;
+ zstream->eof_offset = (uoff_t)-1;
+ zstream->gz = gz;
+
+ i_stream_zlib_init(zstream);
zstream->istream.iostream.close = i_stream_zlib_close;
- zstream->istream.max_buffer_size = DEFAULT_MAX_BUFFER_SIZE;
+ 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;
- /* if it's a file, set the flags properly */
- if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
- zstream->istream.istream.blocking = TRUE;
- zstream->istream.istream.seekable = TRUE;
- }
-
zstream->istream.istream.readable_fd = FALSE;
- return i_stream_create(&zstream->istream, NULL, fd);
+ 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)
+{
+ return i_stream_create_zlib(input, TRUE);
+}
+
+struct istream *i_stream_create_deflate(struct istream *input)
+{
+ return i_stream_create_zlib(input, FALSE);
}
#endif
# define o_stream_create_bzlib NULL
#endif
+#define MAX_INBUF_SIZE (1024*1024)
+
struct zlib_handler {
const char *name;
const char *ext;
bool (*is_compressed)(struct istream *input);
- struct istream *(*create_istream)(int fd);
+ struct istream *(*create_istream)(struct istream *input);
struct ostream *(*create_ostream)(struct ostream *output, int level);
};
static struct zlib_handler zlib_handlers[] = {
{ "gz", ".gz", is_compressed_zlib,
- i_stream_create_zlib, o_stream_create_gz },
+ i_stream_create_gz, o_stream_create_gz },
{ "bz2", ".bz2", is_compressed_bzlib,
- i_stream_create_bzlib, o_stream_create_bz2 }
+ i_stream_create_bz2, o_stream_create_bz2 }
};
static struct zlib_handler *zlib_find_zlib_handler(const char *name)
union mail_module_context *zmail = ZLIB_MAIL_CONTEXT(mail);
struct istream *input;
struct zlib_handler *handler;
- int fd;
if (imail->data.stream != NULL) {
return zmail->super.get_stream(_mail, hdr_size, body_size,
mail_storage_set_critical(_mail->box->storage,
"zlib plugin: Detected %s compression "
"but support not compiled in", handler->ext);
- fd = -1;
- } else {
- fd = dup(i_stream_get_fd(imail->data.stream));
- if (fd == -1) {
- mail_storage_set_critical(_mail->box->storage,
- "zlib plugin: dup() failed: %m");
- }
+ return -1;
}
- imail->data.destroying_stream = TRUE;
- i_stream_unref(&imail->data.stream);
- i_assert(!imail->data.destroying_stream);
-
- if (fd == -1)
- return -1;
- imail->data.stream = handler->create_istream(fd);
+ input = imail->data.stream;
+ imail->data.stream = handler->create_istream(input);
+ i_stream_unref(&input);
}
return index_mail_init_stream(imail, hdr_size, body_size, stream_r);
}
static int zlib_mailbox_open_input(struct mailbox *box)
{
struct zlib_handler *handler;
+ struct istream *input;
int fd;
handler = zlib_get_zlib_handler_ext(box->name);
"open(%s) failed: %m", box->path);
return -1;
}
- box->input = handler->create_istream(fd);
+ input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
+ box->input = handler->create_istream(input);
+ i_stream_unref(&input);
box->flags |= MAILBOX_FLAG_READONLY;
}
return 0;