Mercurial > dovecot > original-hg > dovecot-1.2
changeset 3800:68e7519ebd90 HEAD
Added zlib plugin
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 31 Dec 2005 19:39:16 +0200 |
parents | 87793c933996 |
children | 0420cf130a20 |
files | configure.in src/plugins/Makefile.am src/plugins/zlib/.cvsignore src/plugins/zlib/Makefile.am src/plugins/zlib/istream-zlib.c src/plugins/zlib/istream-zlib.h src/plugins/zlib/zlib-plugin.c src/plugins/zlib/zlib-plugin.h |
diffstat | 8 files changed, 397 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/configure.in Sat Dec 31 19:31:54 2005 +0200 +++ b/configure.in Sat Dec 31 19:39:16 2005 +0200 @@ -1595,6 +1595,7 @@ src/plugins/quota/Makefile src/plugins/imap-quota/Makefile src/plugins/trash/Makefile +src/plugins/zlib/Makefile stamp.h dovecot.spec dovecot-config])
--- a/src/plugins/Makefile.am Sat Dec 31 19:31:54 2005 +0200 +++ b/src/plugins/Makefile.am Sat Dec 31 19:39:16 2005 +0200 @@ -1,1 +1,1 @@ -SUBDIRS = quota imap-quota trash +SUBDIRS = quota imap-quota trash zlib
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/zlib/.cvsignore Sat Dec 31 19:39:16 2005 +0200 @@ -0,0 +1,8 @@ +*.la +*.lo +*.o +.deps +.libs +Makefile +Makefile.in +so_locations
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/zlib/Makefile.am Sat Dec 31 19:39:16 2005 +0200 @@ -0,0 +1,21 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage \ + -I$(top_srcdir)/src/lib-storage/index + +imap_moduledir = $(moduledir)/imap + +lib01_zlib_plugin_la_LDFLAGS = -module -avoid-version + +imap_module_LTLIBRARIES = \ + lib01_zlib_plugin.la + +lib01_zlib_plugin_la_SOURCES = \ + istream-zlib.c \ + zlib-plugin.c + +noinst_HEADERS = \ + istream-zlib.h \ + zlib-plugin.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/zlib/istream-zlib.c Sat Dec 31 19:39:16 2005 +0200 @@ -0,0 +1,257 @@ +/* Copyright (C) 2005 Timo Sirainen */ + +#include "lib.h" +#include "istream-internal.h" +#include "istream-zlib.h" + +#include <zlib.h> + +/* Default maximum buffer size. Seeking backwards is very expensive, so keep + this pretty large */ +#define DEFAULT_MAX_BUFFER_SIZE (1024*1024) + +#define I_STREAM_MIN_SIZE 4096 + +struct zlib_istream { + struct _istream istream; + + size_t max_buffer_size; + + int fd; + gzFile *file; + uoff_t cached_size; + uoff_t seek_offset; + + unsigned int marked:1; +}; + +static void _close(struct _iostream *stream) +{ + struct zlib_istream *zstream = (struct zlib_istream *)stream; + + if (zstream->file != NULL) { + gzclose(zstream->file); + zstream->file = NULL; + } +} + +static void _destroy(struct _iostream *stream __attr_unused__) +{ + struct _istream *_stream = (struct _istream *) stream; + + p_free(_stream->iostream.pool, _stream->w_buffer); +} + +static void _set_max_buffer_size(struct _iostream *stream, size_t max_size) +{ + struct zlib_istream *zstream = (struct zlib_istream *)stream; + + zstream->max_buffer_size = max_size; +} + +static void i_stream_grow_buffer(struct _istream *stream, size_t bytes) +{ + struct zlib_istream *zstream = (struct zlib_istream *)stream; + size_t old_size; + + old_size = stream->buffer_size; + + stream->buffer_size = stream->pos + bytes; + if (stream->buffer_size <= I_STREAM_MIN_SIZE) + stream->buffer_size = I_STREAM_MIN_SIZE; + else { + stream->buffer_size = + pool_get_exp_grown_size(stream->iostream.pool, + old_size, stream->buffer_size); + } + + if (zstream->max_buffer_size > 0 && + stream->buffer_size > zstream->max_buffer_size) + stream->buffer_size = zstream->max_buffer_size; + + stream->buffer = stream->w_buffer = + p_realloc(stream->iostream.pool, stream->w_buffer, + old_size, stream->buffer_size); +} + +static void i_stream_compress(struct _istream *stream) +{ + memmove(stream->w_buffer, stream->w_buffer + stream->skip, + stream->pos - stream->skip); + stream->pos -= stream->skip; + + stream->skip = 0; +} + +static ssize_t _read(struct _istream *stream) +{ + struct zlib_istream *zstream = (struct zlib_istream *)stream; + size_t size; + int ret; + + if (stream->istream.closed) + return -1; + + stream->istream.stream_errno = 0; + + if (stream->pos == stream->buffer_size) { + 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 (zstream->max_buffer_size == 0 || + stream->buffer_size < zstream->max_buffer_size) { + /* buffer is full - grow it */ + i_stream_grow_buffer(stream, I_STREAM_MIN_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 */ + } + } + + size = stream->buffer_size - stream->pos; + + ret = -1; + + i_assert(zstream->seek_offset == stream->istream.v_offset + + (stream->pos - stream->skip)); + ret = gzread(zstream->file, stream->w_buffer + stream->pos, size); + if (ret == 0) { + /* EOF */ + stream->istream.eof = TRUE; + return -1; + } + + if (ret < 0) { + if (errno == EINTR || errno == EAGAIN) + ret = 0; + else { + stream->istream.eof = TRUE; + stream->istream.stream_errno = errno; + return -1; + } + } + + zstream->seek_offset += ret; + stream->pos += ret; + i_assert(ret != 0); + return ret; +} + +static void _seek(struct _istream *stream, uoff_t v_offset, int mark) +{ + struct zlib_istream *zstream = (struct zlib_istream *) stream; + uoff_t start_offset = stream->istream.v_offset - stream->skip; + + stream->istream.stream_errno = 0; + + if (v_offset < start_offset) { + /* have to seek backwards */ + gzseek(zstream->file, v_offset, SEEK_SET); + zstream->seek_offset = v_offset; + + stream->skip = stream->pos = 0; + stream->istream.v_offset = v_offset; + } else 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 (_read(stream) >= 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() 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 *_stat(struct _istream *stream, int exact) +{ + struct zlib_istream *zstream = (struct zlib_istream *) stream; + size_t size; + + if (fstat(zstream->fd, &stream->statbuf) < 0) { + i_error("zlib_istream.fstat() failed: %m"); + return NULL; + } + + if (!exact) + return &stream->statbuf; + + if (zstream->cached_size == (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 (_read(stream) > 0); + + zstream->cached_size = stream->istream.v_offset; + i_stream_seek(&stream->istream, old_offset); + } + stream->statbuf.st_size = zstream->cached_size; + return &stream->statbuf; +} + +static void _sync(struct _istream *stream) +{ + struct zlib_istream *zstream = (struct zlib_istream *) stream; + + zstream->cached_size = (uoff_t)-1; +} + +struct istream *i_stream_create_zlib(int fd, pool_t pool) +{ + struct zlib_istream *zstream; + + zstream = p_new(pool, struct zlib_istream, 1); + zstream->fd = fd; + zstream->file = gzdopen(fd, "r"); + zstream->cached_size = (uoff_t)-1; + zstream->max_buffer_size = DEFAULT_MAX_BUFFER_SIZE; + + zstream->istream.iostream.close = _close; + zstream->istream.iostream.destroy = _destroy; + zstream->istream.iostream.set_max_buffer_size = _set_max_buffer_size; + + zstream->istream.read = _read; + zstream->istream.seek = _seek; + zstream->istream.stat = _stat; + zstream->istream.sync = _sync; + + zstream->istream.istream.seekable = TRUE; + return _i_stream_create(&zstream->istream, pool, fd, 0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/zlib/istream-zlib.h Sat Dec 31 19:39:16 2005 +0200 @@ -0,0 +1,6 @@ +#ifndef __ISTREAM_ZLIB_H +#define __ISTREAM_ZLIB_H + +struct istream *i_stream_create_zlib(int fd, pool_t pool); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/zlib/zlib-plugin.c Sat Dec 31 19:39:16 2005 +0200 @@ -0,0 +1,96 @@ +/* Copyright (C) 2005 Timo Sirainen */ + +#include "lib.h" +#include "array.h" +#include "istream-zlib.h" +#include "home-expand.h" +#include "istream.h" +#include "mail-storage-private.h" +#include "index-storage.h" +#include "zlib-plugin.h" + +struct zlib_mail_storage { + struct mail_storage_vfuncs super; +}; + +#define ZLIB_CONTEXT(obj) \ + *((void **)array_idx_modifyable(&(obj)->module_contexts, \ + zlib_storage_module_id)) + +/* defined by imap, pop3, lda */ +extern void (*hook_mail_storage_created)(struct mail_storage *storage); + +static void (*zlib_next_hook_mail_storage_created) + (struct mail_storage *storage); + +static unsigned int zlib_storage_module_id = 0; +static int zlib_storage_module_id_set = FALSE; + +static const char * +mbox_get_path(struct index_storage *storage, const char *name) +{ + if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 && + (*name == '/' || *name == '~')) + return home_expand(name); + return t_strconcat(storage->dir, "/", name, NULL); +} + +static struct mailbox * +zlib_mailbox_open(struct mail_storage *storage, const char *name, + struct istream *input, enum mailbox_open_flags flags) +{ + struct index_storage *istorage = (struct index_storage *)storage; + struct zlib_mail_storage *qstorage = ZLIB_CONTEXT(storage); + struct mailbox *box; + struct istream *zlib_input = NULL; + size_t len = strlen(name); + + if (input == NULL && strcmp(storage->name, "mbox") == 0 && + len > 3 && strcmp(name + len - 3, ".gz") == 0) { + int fd = open(mbox_get_path(istorage, name), O_RDONLY); + if (fd != -1) { + input = zlib_input = + i_stream_create_zlib(fd, default_pool); + } + } + + box = qstorage->super.mailbox_open(storage, name, input, flags); + + if (zlib_input != NULL) + i_stream_unref(zlib_input); + + return box; +} + +static void zlib_mail_storage_created(struct mail_storage *storage) +{ + struct zlib_mail_storage *qstorage; + + if (zlib_next_hook_mail_storage_created != NULL) + zlib_next_hook_mail_storage_created(storage); + + qstorage = p_new(storage->pool, struct zlib_mail_storage, 1); + qstorage->super = storage->v; + storage->v.mailbox_open = zlib_mailbox_open; + + if (!zlib_storage_module_id_set) { + zlib_storage_module_id = mail_storage_module_id++; + zlib_storage_module_id_set = TRUE; + } + + array_idx_set(&storage->module_contexts, + zlib_storage_module_id, &qstorage); +} + +void zlib_plugin_init(void) +{ + zlib_next_hook_mail_storage_created = + hook_mail_storage_created; + hook_mail_storage_created = zlib_mail_storage_created; +} + +void zlib_plugin_deinit(void) +{ + hook_mail_storage_created = + zlib_next_hook_mail_storage_created; +}