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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/zlib/zlib-plugin.h	Sat Dec 31 19:39:16 2005 +0200
@@ -0,0 +1,7 @@
+#ifndef __ZLIB_PLUGIN_H
+#define __ZLIB_PLUGIN_H
+
+void zlib_plugin_init(void);
+void zlib_plugin_deinit(void);
+
+#endif