diff -r -u -N --exclude=Makefile.in old/configure.in new/configure.in
--- old/configure.in	2006-07-13 03:14:54.410558501 +0200
+++ new/configure.in	2006-07-13 00:50:29.000000000 +0200
@@ -1744,8 +1744,10 @@
 src/lib-imap/Makefile
 src/lib-index/Makefile
 src/lib-mail/Makefile
+src/lib-managesieve/Makefile
 src/lib-ntlm/Makefile
 src/lib-settings/Makefile
+src/lib-sievestorage/Makefile
 src/lib-storage/Makefile
 src/lib-storage/index/Makefile
 src/lib-storage/index/maildir/Makefile
@@ -1759,6 +1761,8 @@
 src/imap/Makefile
 src/imap-login/Makefile
 src/login-common/Makefile
+src/managesieve/Makefile
+src/managesieve-login/Makefile
 src/master/Makefile
 src/pop3/Makefile
 src/pop3-login/Makefile
diff -r -u -N --exclude=Makefile.in old/src/lib-managesieve/Makefile.am new/src/lib-managesieve/Makefile.am
--- old/src/lib-managesieve/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-managesieve/Makefile.am	2006-07-13 00:12:16.000000000 +0200
@@ -0,0 +1,14 @@
+noinst_LIBRARIES = libmanagesieve.a
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-charset \
+	-I$(top_srcdir)/src/lib-mail
+
+libmanagesieve_a_SOURCES = \
+	managesieve-quote.c \
+	managesieve-parser.c 
+
+noinst_HEADERS = \
+	managesieve-quote.h \
+	managesieve-parser.h 
diff -r -u -N --exclude=Makefile.in old/src/lib-managesieve/managesieve-parser.c new/src/lib-managesieve/managesieve-parser.c
--- old/src/lib-managesieve/managesieve-parser.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-managesieve/managesieve-parser.c	2006-07-13 12:10:48.287781907 +0200
@@ -0,0 +1,679 @@
+#include "lib.h"
+#include "istream.h"
+#include "ostream.h"
+#include "strescape.h"
+#include "managesieve-parser.h"
+
+#define is_linebreak(c) \
+	((c) == '\r' || (c) == '\n')
+
+#define LIST_ALLOC_SIZE 7
+
+enum arg_parse_type {
+	ARG_PARSE_NONE = 0,
+	ARG_PARSE_ATOM,
+	ARG_PARSE_STRING,
+	ARG_PARSE_LITERAL,
+	ARG_PARSE_LITERAL_DATA
+};
+
+struct managesieve_parser {
+	/* permanent */
+	pool_t pool;
+	struct istream *input;
+	struct ostream *output;
+	size_t max_line_size;
+	enum managesieve_parser_flags flags;
+
+	/* reset by managesieve_parser_reset(): */
+	size_t line_size;
+	struct managesieve_arg_list *root_list;
+	struct managesieve_arg_list *cur_list;
+
+	enum arg_parse_type cur_type;
+	size_t cur_pos; /* parser position in input buffer */
+
+	int str_first_escape; /* ARG_PARSE_STRING: index to first '\' */
+	uoff_t literal_size; /* ARG_PARSE_LITERAL: string size */
+
+	const char *error;
+
+	unsigned int literal_skip_crlf:1;
+	unsigned int literal_nonsync:1;
+	unsigned int eol:1;
+	unsigned int fatal_error:1;
+};
+
+/* @UNSAFE */
+#define LIST_REALLOC(parser, old_list, new_size) \
+	p_realloc((parser)->pool, old_list, \
+		  sizeof(struct managesieve_arg_list) + \
+		  (old_list == NULL ? 0 : \
+		   sizeof(struct managesieve_arg_list) * (old_list)->alloc), \
+		  sizeof(struct managesieve_arg_list) * (new_size))
+
+static void managesieve_args_realloc(struct managesieve_parser *parser, size_t size)
+{
+	parser->cur_list = LIST_REALLOC(parser, parser->cur_list, size);
+	parser->cur_list->alloc = size;
+
+  parser->root_list = parser->cur_list;
+}
+
+struct managesieve_parser *
+managesieve_parser_create(struct istream *input, struct ostream *output,
+		   size_t max_line_size)
+{
+	struct managesieve_parser *parser;
+
+	parser = i_new(struct managesieve_parser, 1);
+        parser->pool = pool_alloconly_create("MANAGESIEVE parser", 8192);
+	parser->input = input;
+	parser->output = output;
+	parser->max_line_size = max_line_size;
+
+	managesieve_args_realloc(parser, LIST_ALLOC_SIZE);
+	return parser;
+}
+
+void managesieve_parser_destroy(struct managesieve_parser **parser)
+{
+	pool_unref((*parser)->pool);
+	i_free(*parser);
+	*parser = NULL;
+}
+
+void managesieve_parser_reset(struct managesieve_parser *parser)
+{
+	p_clear(parser->pool);
+
+	parser->line_size = 0;
+
+	parser->root_list = NULL;
+	parser->cur_list = NULL;
+
+	parser->cur_type = ARG_PARSE_NONE;
+	parser->cur_pos = 0;
+
+	parser->str_first_escape = 0;
+	parser->literal_size = 0;
+
+	parser->error = NULL;
+
+	parser->literal_skip_crlf = FALSE;
+	parser->eol = FALSE;
+
+	managesieve_args_realloc(parser, LIST_ALLOC_SIZE);
+}
+
+const char *managesieve_parser_get_error(struct managesieve_parser *parser, bool *fatal)
+{
+	*fatal = parser->fatal_error;
+	return parser->error;
+}
+
+/* skip over everything parsed so far, plus the following whitespace */
+static int managesieve_parser_skip_to_next(struct managesieve_parser *parser,
+				    const unsigned char **data,
+				    size_t *data_size)
+{
+	size_t i;
+
+	for (i = parser->cur_pos; i < *data_size; i++) {
+		if ((*data)[i] != ' ')
+			break;
+	}
+
+	parser->line_size += i;
+	i_stream_skip(parser->input, i);
+	parser->cur_pos = 0;
+
+	*data += i;
+	*data_size -= i;
+	return *data_size > 0;
+}
+
+static struct managesieve_arg *managesieve_arg_create(struct managesieve_parser *parser)
+{
+	struct managesieve_arg *arg;
+
+	i_assert(parser->cur_list != NULL);
+
+	/* @UNSAFE */
+	if (parser->cur_list->size == parser->cur_list->alloc)
+		managesieve_args_realloc(parser, parser->cur_list->alloc * 2);
+
+	arg = &parser->cur_list->args[parser->cur_list->size];
+	parser->cur_list->size++;
+
+	return arg;
+}
+
+static void managesieve_parser_save_arg(struct managesieve_parser *parser,
+				 const unsigned char *data, size_t size)
+{
+	struct managesieve_arg *arg;
+
+	arg = managesieve_arg_create(parser);
+
+	switch (parser->cur_type) {
+	case ARG_PARSE_ATOM:
+		/* simply save the string */
+		arg->type = MANAGESIEVE_ARG_ATOM;
+		arg->_data.str = p_strndup(parser->pool, data, size);
+		break;
+	case ARG_PARSE_STRING:
+		/* data is quoted and may contain escapes. */
+		i_assert(size > 0);
+
+		arg->type = MANAGESIEVE_ARG_STRING;
+		arg->_data.str = p_strndup(parser->pool, data+1, size-1);
+
+		/* remove the escapes */
+		if (parser->str_first_escape >= 0 &&
+		    (parser->flags & MANAGESIEVE_PARSE_FLAG_NO_UNESCAPE) == 0) {
+			/* -1 because we skipped the '"' prefix */
+			str_unescape(arg->_data.str +
+				     parser->str_first_escape-1);
+		}
+		break;
+	case ARG_PARSE_LITERAL_DATA:
+		if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) != 0) {
+			/* save literal size */
+			arg->type = parser->literal_nonsync ?
+				MANAGESIEVE_ARG_LITERAL_SIZE_NONSYNC :
+				MANAGESIEVE_ARG_LITERAL_SIZE;
+			arg->_data.literal_size = parser->literal_size;
+		} else if ((parser->flags &
+			    MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE) != 0) {
+			arg->type = MANAGESIEVE_ARG_LITERAL;
+			arg->_data.str = p_strndup(parser->pool, data, size);
+		} else {
+			arg->type = MANAGESIEVE_ARG_STRING;
+			arg->_data.str = p_strndup(parser->pool, data, size);
+		}
+		break;
+	default:
+		i_unreached();
+	}
+
+	parser->cur_type = ARG_PARSE_NONE;
+}
+
+static int is_valid_atom_char(struct managesieve_parser *parser, char chr)
+{
+	if (IS_ATOM_SPECIAL((unsigned char)chr)) {
+		parser->error = "Invalid characters in atom";
+		return FALSE;
+	} else if ((chr & 0x80) != 0) {
+		parser->error = "8bit data in atom";
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int managesieve_parser_read_atom(struct managesieve_parser *parser,
+				 const unsigned char *data, size_t data_size)
+{
+	size_t i;
+
+	/* read until we've found space, CR or LF. */
+	for (i = parser->cur_pos; i < data_size; i++) {
+		if (data[i] == ' ' || data[i] == ')' ||
+			 is_linebreak(data[i])) {
+			managesieve_parser_save_arg(parser, data, i);
+			break;
+		} else if (!is_valid_atom_char(parser, data[i]))
+			return FALSE;
+	}
+
+	parser->cur_pos = i;
+	return parser->cur_type == ARG_PARSE_NONE;
+}
+
+static int managesieve_parser_read_string(struct managesieve_parser *parser,
+				   const unsigned char *data, size_t data_size)
+{
+	size_t i;
+	int utf8_len;
+
+	/* QUOTED-CHAR        = SAFE-UTF8-CHAR / "\" QUOTED-SPECIALS
+	 * quoted             = <"> *QUOTED-CHAR <">
+	 *                    ;; limited to 1024 octets between the <">s
+	 */
+
+	/* read until we've found non-escaped ", CR or LF */
+	for (i = parser->cur_pos; i < data_size; i++) {
+		if (data[i] == '"') {
+			managesieve_parser_save_arg(parser, data, i);
+
+			i++; /* skip the trailing '"' too */
+			break;
+		}
+
+		if (data[i] == '\\') {
+			if (i+1 == data_size) {
+				/* known data ends with '\' - leave it to
+				   next time as well if it happens to be \" */
+				break;
+			}
+
+			/* save the first escaped char */
+			if (parser->str_first_escape < 0)
+				parser->str_first_escape = i;
+
+			/* skip the escaped char */
+			i++;
+
+			if ( !IS_QUOTED_SPECIAL(data[i]) ) {
+				parser->error = "Escaped quoted-string character is not a QUOTED-SPECIAL.";
+				return FALSE;
+			}
+
+			continue;
+		}
+
+		/* Enforce valid UTF-8
+		 */
+		if ( (utf8_len = UTF8_LEN(data[i])) == 0 ) {
+			parser->error = "String contains invalid character.";
+			return FALSE;
+		}
+		
+		if ( utf8_len > 1 ) {
+			bool overlong = FALSE;
+
+			if ( (i+utf8_len-1) >= data_size ) {
+				/* Known data ends in the middle of a UTF-8 character;
+				 * leave it to next time.
+				 */
+				break;
+			}
+
+			/* Check for overlong UTF-8 sequences */
+			switch (utf8_len) {
+			case 2:
+				if (!(data[i] & 0x1E)) overlong = TRUE;
+				break;
+			case 3:	
+				if (!(data[i] & 0x0F) && !(data[i+1] & 0x20)) overlong = TRUE;
+				break;
+			case 4:
+				if (!(data[i] & 0x07) && !(data[i+1] & 0x30)) overlong = TRUE;				
+				break;
+			case 5:
+				if (!(data[i] & 0x03) && !(data[i+1] & 0x38)) overlong = TRUE;
+				break;				
+			case 6:
+				if (!(data[i] & 0x01) && !(data[i+1] & 0x3C)) overlong = TRUE;
+				break;				
+			default:
+				i_unreached();
+			} 
+
+			if ( overlong ) {
+				parser->error = "String contains invalid/overlong UTF-8 character.";
+				return FALSE;
+			}
+
+			i++;
+			utf8_len--;
+	
+			/* Parse the series of UTF8_1 characters */
+			for (; utf8_len > 0; utf8_len--, i++ ) {  
+				if (!IS_UTF8_1(data[i])) {
+					parser->error = "String contains invalid UTF-8 character.";
+			    return FALSE;
+				}
+			}
+		}
+	}
+
+	parser->cur_pos = i;
+	return parser->cur_type == ARG_PARSE_NONE;
+}
+
+static int managesieve_parser_literal_end(struct managesieve_parser *parser)
+{
+	if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) == 0) {
+		if (parser->line_size >= parser->max_line_size ||
+		    parser->literal_size >
+		    parser->max_line_size - parser->line_size) {
+			/* too long string, abort. */
+			parser->error = "Literal size too large";
+			parser->fatal_error = TRUE;
+			return FALSE;
+		}
+
+		if (!parser->literal_nonsync) {
+			parser->error = "MANAGESIEVE does not support literal synchronous mode.";
+			return FALSE;
+		}
+	}
+
+	parser->cur_type = ARG_PARSE_LITERAL_DATA;
+	parser->literal_skip_crlf = TRUE;
+
+	parser->cur_pos = 0;
+	return TRUE;
+}
+
+static int managesieve_parser_read_literal(struct managesieve_parser *parser,
+				    const unsigned char *data,
+				    size_t data_size)
+{
+	size_t i, prev_size;
+
+	/* expecting digits + "}" */
+	for (i = parser->cur_pos; i < data_size; i++) {
+		if (data[i] == '}') {
+			parser->line_size += i+1;
+			i_stream_skip(parser->input, i+1);
+
+			return managesieve_parser_literal_end(parser);
+		}
+
+		if (parser->literal_nonsync) {
+			parser->error = "Expecting '}' after '+'";
+			return FALSE;
+		}
+
+		if (data[i] == '+') {
+			parser->literal_nonsync = TRUE;
+			continue;
+		}
+
+		if (data[i] < '0' || data[i] > '9') {
+			parser->error = "Invalid literal size";
+			return FALSE;
+		}
+
+		prev_size = parser->literal_size;
+		parser->literal_size = parser->literal_size*10 + (data[i]-'0');
+
+		if (parser->literal_size < prev_size) {
+			/* wrapped around, abort. */
+			parser->error = "Literal size too large";
+			return FALSE;
+		}
+	}
+
+	parser->cur_pos = i;
+	return FALSE;
+}
+
+static int managesieve_parser_read_literal_data(struct managesieve_parser *parser,
+					 const unsigned char *data,
+					 size_t data_size)
+{
+	if (parser->literal_skip_crlf) {
+
+		/* skip \r\n or \n, anything else gives an error */
+		if (data_size == 0)
+			return FALSE;
+
+		if (*data == '\r') {
+			parser->line_size++;
+			data++; data_size--;
+			i_stream_skip(parser->input, 1);
+
+			if (data_size == 0)
+				return FALSE;
+		}
+
+		if (*data != '\n') {
+			parser->error = "Missing LF after literal size";
+			return FALSE;
+		}
+
+		parser->line_size++;
+		data++; data_size--;
+		i_stream_skip(parser->input, 1);
+
+		parser->literal_skip_crlf = FALSE;
+
+		i_assert(parser->cur_pos == 0);
+	}
+
+	if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) == 0) {
+		/* now we just wait until we've read enough data */
+		if (data_size < parser->literal_size) {
+			return FALSE;
+		} else {
+			managesieve_parser_save_arg(parser, data,
+					     (size_t)parser->literal_size);
+			parser->cur_pos = (size_t)parser->literal_size;
+			return TRUE;
+		}
+	} else {
+		/* we want to save only literal size, not the literal itself. */
+		parser->eol = TRUE;
+		managesieve_parser_save_arg(parser, NULL, 0);
+		return TRUE;
+	}
+}
+
+/* Returns TRUE if argument was fully processed. Also returns TRUE if
+   an argument inside a list was processed. */
+static int managesieve_parser_read_arg(struct managesieve_parser *parser)
+{
+	const unsigned char *data;
+	size_t data_size;
+
+	data = i_stream_get_data(parser->input, &data_size);
+	if (data_size == 0)
+		return FALSE;
+
+	while (parser->cur_type == ARG_PARSE_NONE) {
+		/* we haven't started parsing yet */
+		if (!managesieve_parser_skip_to_next(parser, &data, &data_size))
+			return FALSE;
+		i_assert(parser->cur_pos == 0);
+
+		switch (data[0]) {
+		case '\r':
+		case '\n':
+			/* unexpected end of line */
+			parser->eol = TRUE;
+			return FALSE;
+		case '"':
+			parser->cur_type = ARG_PARSE_STRING;
+			parser->str_first_escape = -1;
+			break;
+		case '{':
+			parser->cur_type = ARG_PARSE_LITERAL;
+			parser->literal_size = 0;
+			parser->literal_nonsync = FALSE;
+			break;
+		default:
+			if (!is_valid_atom_char(parser, data[0]))
+				return FALSE;
+			parser->cur_type = ARG_PARSE_ATOM;
+			break;
+		}
+
+		parser->cur_pos++;
+	}
+
+	i_assert(data_size > 0);
+
+	switch (parser->cur_type) {
+	case ARG_PARSE_ATOM:
+		if (!managesieve_parser_read_atom(parser, data, data_size))
+			return FALSE;
+		break;
+	case ARG_PARSE_STRING:
+		if (!managesieve_parser_read_string(parser, data, data_size))
+			return FALSE;
+		break;
+	case ARG_PARSE_LITERAL:
+		if (!managesieve_parser_read_literal(parser, data, data_size))
+			return FALSE;
+
+		/* pass through to parsing data. since input->skip was
+		   modified, we need to get the data start position again. */
+		data = i_stream_get_data(parser->input, &data_size);
+
+		/* fall through */
+	case ARG_PARSE_LITERAL_DATA:
+		if (!managesieve_parser_read_literal_data(parser, data, data_size))
+			return FALSE;
+		break;
+	default:
+		i_unreached();
+	}
+
+	i_assert(parser->cur_type == ARG_PARSE_NONE);
+	return TRUE;
+}
+
+/* ARG_PARSE_NONE checks that last argument isn't only partially parsed. */
+#define IS_UNFINISHED(parser) \
+        ((parser)->cur_type != ARG_PARSE_NONE || \
+	 (parser)->cur_list != parser->root_list)
+
+static int finish_line(struct managesieve_parser *parser, unsigned int count,
+		       struct managesieve_arg **args)
+{
+	parser->line_size += parser->cur_pos;
+	i_stream_skip(parser->input, parser->cur_pos);
+	parser->cur_pos = 0;
+
+	if (count >= parser->root_list->alloc) {
+		/* unused arguments must be NIL-filled. */
+		parser->root_list =
+			LIST_REALLOC(parser, parser->root_list, count+1);
+		parser->root_list->alloc = count+1;
+	}
+
+	parser->root_list->args[parser->root_list->size].type = MANAGESIEVE_ARG_EOL;
+
+	*args = parser->root_list->args;
+	return parser->root_list->size;
+}
+
+int managesieve_parser_read_args(struct managesieve_parser *parser, unsigned int count,
+			  enum managesieve_parser_flags flags, struct managesieve_arg **args)
+{
+	parser->flags = flags;
+
+	while (!parser->eol && (count == 0 || parser->root_list->size < count ||
+				IS_UNFINISHED(parser))) {
+		if (!managesieve_parser_read_arg(parser))
+			break;
+
+		if (parser->line_size > parser->max_line_size) {
+			parser->error = "MANAGESIEVE command line too large";
+			break;
+		}
+	}
+
+	if (parser->error != NULL) {
+		/* error, abort */
+		parser->line_size += parser->cur_pos;
+		i_stream_skip(parser->input, parser->cur_pos);
+		parser->cur_pos = 0;
+		*args = NULL;
+		return -1;
+	} else if ((!IS_UNFINISHED(parser) && count > 0 &&
+		    parser->root_list->size >= count) || parser->eol) {
+		/* all arguments read / end of line. */
+                return finish_line(parser, count, args);
+	} else {
+		/* need more data */
+		*args = NULL;
+		return -2;
+	}
+}
+
+int managesieve_parser_finish_line(struct managesieve_parser *parser, unsigned int count,
+			    enum managesieve_parser_flags flags,
+			    struct managesieve_arg **args)
+{
+	const unsigned char *data;
+	size_t data_size;
+	int ret;
+
+	ret = managesieve_parser_read_args(parser, count, flags, args);
+	if (ret == -2) {
+		/* we should have noticed end of everything except atom */
+		if (parser->cur_type == ARG_PARSE_ATOM) {
+			data = i_stream_get_data(parser->input, &data_size);
+			managesieve_parser_save_arg(parser, data, data_size);
+		}
+	}
+	return finish_line(parser, count, args);
+}
+
+const char *managesieve_parser_read_word(struct managesieve_parser *parser)
+{
+	const unsigned char *data;
+	size_t i, data_size;
+
+	data = i_stream_get_data(parser->input, &data_size);
+
+	for (i = 0; i < data_size; i++) {
+		if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n')
+			break;
+	}
+
+	if (i < data_size) {
+		data_size = i + (data[i] == ' ' ? 1 : 0);
+		parser->line_size += data_size;
+		i_stream_skip(parser->input, data_size);
+		return p_strndup(parser->pool, data, i);
+	} else {
+		return NULL;
+	}
+}
+
+const char *managesieve_arg_string(struct managesieve_arg *arg)
+{
+	if (arg->type == MANAGESIEVE_ARG_STRING) 
+		return arg->_data.str;
+
+	return NULL;
+}
+
+int managesieve_arg_number
+	(struct managesieve_arg *arg, uoff_t *number)
+{
+	int i = 0;
+	const char *data;
+
+	*number = 0;
+
+	if (arg->type == MANAGESIEVE_ARG_ATOM) {
+		data = arg->_data.str;
+		while (data[i] != '\0') {
+			if (data[i] < '0' || data[i] > '9')
+				return -1;
+	
+			*number = (*number)*10 + (data[i] -'0');
+			i++;
+		}
+    
+		return 1;
+	}
+
+	return -1;
+}
+
+char *_managesieve_arg_str_error(const struct managesieve_arg *arg)
+{
+	i_panic("Tried to access managesieve_arg type %d as string", arg->type);
+	return NULL;
+}
+
+uoff_t _managesieve_arg_literal_size_error(const struct managesieve_arg *arg)
+{
+	i_panic("Tried to access managesieve_arg type %d as literal size", arg->type);
+	return 0;
+}
+
+struct managesieve_arg_list *_managesieve_arg_list_error(const struct managesieve_arg *arg)
+{
+	i_panic("Tried to access managesieve_arg type %d as list", arg->type);
+	return NULL;
+}
diff -r -u -N --exclude=Makefile.in old/src/lib-managesieve/managesieve-parser.h new/src/lib-managesieve/managesieve-parser.h
--- old/src/lib-managesieve/managesieve-parser.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-managesieve/managesieve-parser.h	2006-07-13 12:11:17.624186146 +0200
@@ -0,0 +1,190 @@
+#ifndef __MANAGESIEVE_PARSER_H
+#define __MANAGESIEVE_PARSER_H
+
+/*
+ * QUOTED-SPECIALS    = <"> / "\"
+ */
+#define IS_QUOTED_SPECIAL(c) \
+	((c) == '"' || (c) == '\\')
+
+/* 
+ * ATOM-SPECIALS      = "(" / ")" / "{" / SP / CTL / QUOTED-SPECIALS
+ */
+#define IS_ATOM_SPECIAL(c) \
+	((c) == '(' || (c) == ')' || (c) == '{' || \
+	 (c) <= 32 || (c) == 0x7f || \
+	 IS_QUOTED_SPECIAL(c)) 
+
+/* 
+ * CHAR               = %x01-7F
+ */
+#define IS_CHAR(c) \
+	(((c) & 0x80) == 0)
+
+/* 
+ * TEXT-CHAR          = %x01-09 / %x0B-0C / %x0E-7F
+ *                       ;; any CHAR except CR and LF
+ */
+#define IS_TEXT_CHAR(c) \
+	(IS_CHAR(c) && (c) != '\r' && (c) != '\n')
+
+/*
+ * SAFE-CHAR          = %x01-09 / %x0B-0C / %x0E-21 /
+ *                      %x23-5B / %x5D-7F
+ *                      ;; any TEXT-CHAR except QUOTED-SPECIALS
+ */
+#define IS_SAFE_CHAR(c) \
+	(IS_TEXT_CHAR(c) && !IS_QUOTED_SPECIAL(c))
+
+/* UTF8-1             = %x80-BF
+ */
+#define IS_UTF8_1(c) \
+	(((c) & 0xC0) == 0x80)
+
+/* UTF8-2             = %xC0-DF UTF8-1
+ */
+#define IS_UTF8_2S(c) \
+  (((c) & 0xE0) == 0xC0)
+
+/* UTF8-3             = %xE0-EF 2UTF8-1
+ */
+#define IS_UTF8_3S(c) \
+  (((c) & 0xF0) == 0xE0)
+
+/* UTF8-4             = %xF0-F7 3UTF8-1
+ */
+#define IS_UTF8_4S(c) \
+  (((c) & 0xF8) == 0xF0)
+
+/* UTF8-5             = %xF8-FB 4UTF8-1
+ */
+#define IS_UTF8_5S(c) \
+  (((c) & 0xFC) == 0xF8)
+
+/* UTF8-6             = %xFC-FD 5UTF8-1
+ */
+#define IS_UTF8_6S(c) \
+  (((c) & 0xFE) == 0xFC)
+
+/* SAFE-UTF8-CHAR     = SAFE-CHAR / UTF8-2 / UTF8-3 / UTF8-4 /
+ *                      UTF8-5 / UTF8-6
+ */
+#define UTF8_LEN(c) \
+  ( IS_SAFE_CHAR(c) ? 1 : \
+    IS_UTF8_2S(c) ? 2 : \
+    IS_UTF8_3S(c) ? 3 : \
+    IS_UTF8_4S(c) ? 4 : \
+    IS_UTF8_5S(c) ? 5 : \
+    IS_UTF8_6S(c) ? 6 : 0 )
+
+enum managesieve_parser_flags {
+	/* Set this flag if you wish to read only size of literal argument
+	   and not convert literal into string. Useful when you need to deal
+	   with large literal sizes. The literal must be the last read
+	   parameter. */
+	MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE	= 0x01,
+	/* Don't remove '\' chars from string arguments */
+	MANAGESIEVE_PARSE_FLAG_NO_UNESCAPE	= 0x02,
+	/* Return literals as MANAGESIEVE_ARG_LITERAL instead of MANAGESIEVE_ARG_STRING */
+	MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE	= 0x04
+};
+
+enum managesieve_arg_type {
+	MANAGESIEVE_ARG_ATOM = 0,
+	MANAGESIEVE_ARG_STRING,
+
+	/* literals are returned as MANAGESIEVE_ARG_STRING by default */
+	MANAGESIEVE_ARG_LITERAL,
+	MANAGESIEVE_ARG_LITERAL_SIZE,
+	MANAGESIEVE_ARG_LITERAL_SIZE_NONSYNC,
+
+	MANAGESIEVE_ARG_EOL /* end of argument list */
+};
+
+struct managesieve_parser;
+
+struct managesieve_arg {
+	enum managesieve_arg_type type;
+
+	union {
+		char *str;
+		uoff_t literal_size;
+	} _data;
+};
+
+#define MANAGESIEVE_ARG_STR(arg) \
+	((arg)->type == MANAGESIEVE_ARG_STRING || \
+	 (arg)->type == MANAGESIEVE_ARG_LITERAL ? \
+	 (arg)->_data.str : _managesieve_arg_str_error(arg))
+
+#define MANAGESIEVE_ARG_LITERAL_SIZE(arg) \
+	(((arg)->type == MANAGESIEVE_ARG_LITERAL_SIZE || \
+	 (arg)->type == MANAGESIEVE_ARG_LITERAL_SIZE_NONSYNC) ? \
+	 (arg)->_data.literal_size : _managesieve_arg_literal_size_error(arg))
+
+struct managesieve_arg_list {
+	size_t size, alloc;
+	struct managesieve_arg args[1]; /* variable size */
+};
+
+
+/* Create new MANAGESIEVE argument parser. output is used for sending command
+   continuation requests for literals.
+
+   max_line_size can be used to approximately limit the maximum amount of
+   memory that gets allocated when parsing a line. Input buffer size limits
+   the maximum size of each parsed token.
+
+   Usually the largest lines are large only because they have a one huge
+   message set token, so you'll probably want to keep input buffer size the
+   same as max_line_size. That means the maximum memory usage is around
+   2 * max_line_size. */
+struct managesieve_parser *
+managesieve_parser_create(struct istream *input, struct ostream *output,
+		   size_t max_line_size);
+void managesieve_parser_destroy(struct managesieve_parser **parser);
+
+/* Reset the parser to initial state. */
+void managesieve_parser_reset(struct managesieve_parser *parser);
+
+/* Return the last error in parser. fatal is set to TRUE if there's no way to
+   continue parsing, currently only if too large non-sync literal size was
+   given. */
+const char *managesieve_parser_get_error(struct managesieve_parser *parser, bool *fatal);
+
+/* Read a number of arguments. This function doesn't call i_stream_read(), you
+   need to do that. Returns number of arguments read (may be less than count
+   in case of EOL), -2 if more data is needed or -1 if error occurred.
+
+   count-sized array of arguments are stored into args when return value is
+   0 or larger. If all arguments weren't read, they're set to NIL. count
+   can be set to 0 to read all arguments in the line. Last element in
+   args is always of type MANAGESIEVE_ARG_EOL. */
+int managesieve_parser_read_args(struct managesieve_parser *parser, unsigned int count,
+			  enum managesieve_parser_flags flags, struct managesieve_arg **args);
+
+/* just like managesieve_parser_read_args(), but assume \n at end of data in
+   input stream. */
+int managesieve_parser_finish_line(struct managesieve_parser *parser, unsigned int count,
+			    enum managesieve_parser_flags flags,
+			    struct managesieve_arg **args);
+
+/* Read one word - used for reading tag and command name.
+   Returns NULL if more data is needed. */
+const char *managesieve_parser_read_word(struct managesieve_parser *parser);
+
+/* Returns the managesieve argument as string. If it is no string this returns NULL */
+const char *managesieve_arg_string(struct managesieve_arg *arg);
+
+/* Returns 1 if the argument is a number. If it is no number this returns -1.
+ * The number itself is stored in *number.
+ */
+int managesieve_arg_number
+  (struct managesieve_arg *arg, uoff_t *number);
+
+/* Error functions */
+char *_managesieve_arg_str_error(const struct managesieve_arg *arg);
+uoff_t _managesieve_arg_literal_size_error(const struct managesieve_arg *arg);
+struct managesieve_arg_list *_managesieve_arg_list_error(const struct managesieve_arg *arg);
+
+#endif
diff -r -u -N --exclude=Makefile.in old/src/lib-managesieve/managesieve-quote.c new/src/lib-managesieve/managesieve-quote.c
--- old/src/lib-managesieve/managesieve-quote.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-managesieve/managesieve-quote.c	2006-07-13 12:14:03.702474979 +0200
@@ -0,0 +1,176 @@
+#include "lib.h"
+#include "str.h"
+#include "managesieve-parser.h"
+#include "managesieve-quote.h"
+
+/* Turn the value string into a valid MANAGESIEVE string or literal, no matter 
+ * what. QUOTED-SPECIALS are escaped, but any invalid (UTF-8) character
+ * is simply removed. Linebreak characters are not considered invalid, but
+ * they do force the generation of a string literal.
+ */
+void managesieve_quote_append(string_t *str, const unsigned char *value,
+		       size_t value_len, bool compress_lwsp)
+{
+	size_t i, extra = 0;
+	bool 
+		last_lwsp = TRUE, 
+		literal = FALSE, 
+		modify = FALSE,
+		escape = FALSE;
+	int utf8_len;
+
+ 	if (value == NULL) {
+		str_append(str, "\"\"");
+		return;
+	}
+
+	if (value_len == (size_t)-1)
+		value_len = strlen((const char *) value);
+
+	for (i = 0; i < value_len; i++) {
+		switch (value[i]) {
+		case ' ':
+		case '\t':
+			if (last_lwsp && compress_lwsp) {
+				modify = TRUE;
+				extra++;
+			}
+			last_lwsp = TRUE;
+			break;
+		case '"':
+		case '\\':
+			escape = TRUE;
+			last_lwsp = FALSE;
+			break;
+		case 13:
+		case 10:
+			literal = TRUE;
+			last_lwsp = TRUE;
+			break;
+		default:
+			/* Enforce valid UTF-8
+			 */
+			if ( (utf8_len=UTF8_LEN(value[i])) == 0 ) {
+				modify = TRUE;
+				extra++;
+				break;
+			}
+
+			if ( utf8_len > 1 ) {
+				int c = utf8_len - 1;
+
+		 		if ( (i+utf8_len-1) >= value_len ) {
+				  	/* Value ends in the middle of a UTF-8 character;
+					 * Kill the partial UTF-8 character
+					 */
+				  	extra += i + utf8_len - value_len;
+					modify = TRUE;
+					break;        	
+				}
+
+				/* Parse the series of UTF8_1 characters */
+				for (i++; c > 0; c--, i++ ) {
+					if (!IS_UTF8_1(value[i])) {
+						extra += utf8_len - c;
+						modify = TRUE;
+						break;
+					}
+				}
+			}
+   			
+			last_lwsp = FALSE;
+		}
+	}
+
+	if (!literal) {
+		/* no linebreak chars, return as (escaped) "string" */
+		str_append_c(str, '"');
+	} else {
+		/* return as literal */
+		str_printfa(str, "{%"PRIuSIZE_T"}\r\n", value_len - extra);
+	}
+
+	if (!modify && (literal || !escape))
+		str_append_n(str, value, value_len);
+	else {
+		last_lwsp = TRUE;
+		for (i = 0; i < value_len; i++) {
+			switch (value[i]) {
+			case '"':
+			case '\\':
+				last_lwsp = FALSE;
+				if (!literal) 
+					str_append_c(str, '\\');
+				str_append_c(str, value[i]);
+				break;
+			case ' ':
+			case '\t':
+				if (!last_lwsp || !compress_lwsp)
+					str_append_c(str, ' ');
+				last_lwsp = TRUE;
+				break;
+			case 13:
+			case 10:
+				last_lwsp = TRUE;
+				str_append_c(str, value[i]);
+				break;
+			default:
+	  			/* Enforce valid UTF-8
+				 */
+				if ( (utf8_len=UTF8_LEN(value[i])) == 0 ) 
+					break;
+      
+				if ( utf8_len > 1 ) {
+					int c = utf8_len - 1;
+					int j;
+
+					if ( (i+utf8_len-1) >= value_len ) {
+						/* Value ends in the middle of a UTF-8 character;
+						 * Kill the partial character
+						 */
+					 	i = value_len;
+						break;
+					}
+
+					/* Parse the series of UTF8_1 characters */
+					for (j = i+1; c > 0; c--, j++ ) {
+						if (!IS_UTF8_1(value[j])) {
+							/* Skip until after this erroneous character */
+							i = j;
+							break;
+						}
+					}
+
+					/* Append the UTF-8 character. Last octet is done later */
+					c = utf8_len - 1;
+					for (; c > 0; c--, i++ ) 
+						str_append_c(str, value[i]);
+				}
+     
+				last_lwsp = FALSE;
+				str_append_c(str, value[i]);
+				break;
+			}
+		}
+	}
+
+	if (!literal)
+		str_append_c(str, '"');
+}
+
+char *managesieve_quote(pool_t pool, const unsigned char *value, size_t value_len)
+{
+	string_t *str;
+	char *ret;
+
+	if (value == NULL)
+		return "\"\"";
+
+	t_push();
+	str = t_str_new(value_len + MAX_INT_STRLEN + 5);
+	managesieve_quote_append(str, value, value_len, TRUE);
+	ret = p_strndup(pool, str_data(str), str_len(str));
+	t_pop();
+
+	return ret;
+}
diff -r -u -N --exclude=Makefile.in old/src/lib-managesieve/managesieve-quote.h new/src/lib-managesieve/managesieve-quote.h
--- old/src/lib-managesieve/managesieve-quote.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-managesieve/managesieve-quote.h	2006-07-13 00:12:16.000000000 +0200
@@ -0,0 +1,17 @@
+#ifndef __IMAP_QUOTE_H
+#define __IMAP_QUOTE_H
+
+/* Return value suitable for sending to client, either as quoted-string or
+   literal. Note that this also converts TABs into spaces, multiple spaces
+   into single space and NULs to #128. */
+char *managesieve_quote(pool_t pool, const unsigned char *value, size_t value_len);
+
+/* Append to existing string. */
+void managesieve_quote_append(string_t *str, const unsigned char *value,
+		       size_t value_len, bool compress_lwsp);
+
+#define managesieve_quote_append_string(str, value, compress_lwsp) \
+	managesieve_quote_append(str, (const unsigned char *)(value), \
+			  (size_t)-1, compress_lwsp)
+
+#endif
diff -r -u -N --exclude=Makefile.in old/src/lib-sievestorage/Makefile.am new/src/lib-sievestorage/Makefile.am
--- old/src/lib-sievestorage/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-sievestorage/Makefile.am	2006-07-13 00:12:10.000000000 +0200
@@ -0,0 +1,17 @@
+noinst_LIBRARIES = libsievestorage.a
+
+INCLUDES = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-mail 
+
+libsievestorage_a_SOURCES = \
+	sieve-save.c \
+	sieve-script.c \
+	sieve-list.c \
+	sieve-storage.c 
+
+noinst_HEADERS = \
+	sieve-save.h \
+	sieve-script.h \
+	sieve-list.h \
+	sieve-storage.h 
diff -r -u -N --exclude=Makefile.in old/src/lib-sievestorage/sieve-list.c new/src/lib-sievestorage/sieve-list.c
--- old/src/lib-sievestorage/sieve-list.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-sievestorage/sieve-list.c	2006-07-13 12:15:48.639921513 +0200
@@ -0,0 +1,111 @@
+#include "lib.h"
+#include "str.h"
+#include "sieve-storage.h"
+#include "sieve-script.h"
+#include "sieve-list.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+struct sieve_list_context {
+	pool_t pool;
+	struct sieve_storage *storage;
+
+	const char *active;
+	const char *dir;
+	DIR *dirp;
+
+	unsigned int seen_active:1; // Just present for assertions
+};
+
+struct sieve_list_context *sieve_storage_list_init
+	(struct sieve_storage *storage)
+{	
+	struct sieve_list_context *ctx;
+	const char *active;
+	const char *dir;
+	pool_t pool;
+	DIR *dirp;
+
+	t_push();
+	dir = t_strconcat(storage->dir, "/sieve", NULL);
+
+	/* Open the directory */
+	if ( (dirp = opendir(dir)) == NULL ) {
+		sieve_storage_set_critical(storage, "opendir(%s) failed: %m",
+					   dir);
+		t_pop();
+		return NULL;
+	}
+
+	/* Get the name of the active script */
+	if ( (active = sieve_storage_get_active_script(storage)) 
+		== NULL ) {
+		t_pop();
+		return NULL;
+	}
+
+	pool = pool_alloconly_create("sieve_list_context", 4096);
+	ctx = p_new(pool, struct sieve_list_context, 1);
+	ctx->pool = pool;
+	ctx->storage = storage;
+	ctx->dirp = dirp;
+	ctx->dir = p_strdup(pool, dir);
+	ctx->active = p_strdup(pool, active);
+	ctx->seen_active = FALSE;
+
+	t_pop();
+
+	return ctx;
+}
+
+const char *sieve_storage_list_next
+	(struct sieve_list_context *ctx, bool *active)
+{
+	struct dirent *dp;
+	const char *scriptname;
+
+	*active = FALSE;
+
+	for (;;) {
+		if ( (dp = readdir(ctx->dirp)) == NULL )
+			return NULL;
+
+		scriptname = sieve_storage_file_get_scriptname
+			(ctx->storage, dp->d_name);	
+		
+		if (scriptname != NULL )
+			break;
+	}
+
+	if ( ctx->active != NULL && 
+		strcmp(scriptname, ctx->active) == 0 ) {
+		*active = TRUE;
+		ctx->active = NULL;
+	}
+
+	return scriptname;
+}
+
+int sieve_storage_list_deinit(struct sieve_list_context **ctx)
+{
+	if (closedir((*ctx)->dirp) < 0) {
+		sieve_storage_set_critical((*ctx)->storage, "closedir(%s) failed: %m",
+					   (*ctx)->dir);
+
+		pool_unref((*ctx)->pool);
+		*ctx = NULL;
+		return -1;
+	}
+
+	pool_unref((*ctx)->pool);
+	*ctx = NULL;
+	return 1;
+}
+
+
+	
+    
diff -r -u -N --exclude=Makefile.in old/src/lib-sievestorage/sieve-list.h new/src/lib-sievestorage/sieve-list.h
--- old/src/lib-sievestorage/sieve-list.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-sievestorage/sieve-list.h	2006-07-13 00:12:10.000000000 +0200
@@ -0,0 +1,30 @@
+#ifndef __SIEVE_LIST_H
+#define __SIEVE_LIST_H
+
+#include "lib.h"
+#include "str.h"
+#include "sieve-storage.h"
+#include "sieve-list.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+struct sieve_list_context;
+
+/* Create a context for listing the scripts in the storage */
+struct sieve_list_context *sieve_storage_list_init
+	(struct sieve_storage *storage);
+
+/* Get the next script in the storage. */
+const char *sieve_storage_list_next(struct sieve_list_context *ctx, bool *active);
+
+/* Destroy the listing context */
+int sieve_storage_list_deinit(struct sieve_list_context **ctx);
+
+#endif
+
+
+	
+    
diff -r -u -N --exclude=Makefile.in old/src/lib-sievestorage/sieve-save.c new/src/lib-sievestorage/sieve-save.c
--- old/src/lib-sievestorage/sieve-save.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-sievestorage/sieve-save.c	2006-07-13 12:24:16.259140313 +0200
@@ -0,0 +1,267 @@
+#include "lib.h"
+#include "hostpid.h"
+#include "ioloop.h"
+#include "array.h"
+#include "buffer.h"
+#include "ostream.h"
+#include "ostream-crlf.h"
+#include "str.h"
+#include "sieve-storage.h"
+#include "sieve-save.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <sys/stat.h>
+
+struct sieve_save_context {
+	pool_t pool;
+
+	struct sieve_storage *storage;
+	const char *scriptname;
+
+	struct istream *input;
+	struct ostream *output;
+	int fd;
+	const char *tmp_path;
+
+	unsigned int failed:1;
+	unsigned int moving:1;
+	unsigned int finished:1;
+};
+
+static const char *sieve_generate_tmp_filename(const char *scriptname, const struct timeval *tv)
+{
+	static unsigned int create_count = 0;
+	static time_t first_stamp = 0;
+
+	if (first_stamp == 0 || first_stamp == ioloop_time) {
+	  	/* it's possible that within last second another process had
+		   the same PID as us. Use usecs to make sure we don't create
+		   duplicate base name. */
+		first_stamp = ioloop_time;
+		return t_strdup_printf
+		  ("%s-%s.P%sQ%uM%s.%s",
+		   scriptname,
+		   dec2str(tv->tv_sec), my_pid,
+		   create_count++,
+		   dec2str(tv->tv_usec), my_hostname);
+	} else {
+		/* Don't bother with usecs. Saves a bit space :) */
+		return t_strdup_printf
+		  ("%s-%s.P%sQ%u.%s",
+		   scriptname,
+		   dec2str(tv->tv_sec), my_pid,
+		   create_count++, my_hostname);
+	}
+}
+
+static int sieve_create_tmp(struct sieve_storage *storage, const char *scriptname, const char **fname)
+{
+ 	const char *path, *tmp_fname;
+	struct stat st;
+	struct timeval *tv, tv_now;
+	pool_t pool;
+	int fd;
+
+	tv = &ioloop_timeval;
+	pool = pool_alloconly_create("script_tmp", 4096);
+	for (;;) {
+		p_clear(pool);
+		tmp_fname = sieve_generate_tmp_filename(scriptname, tv);
+
+		path = p_strconcat(pool, storage->dir, "/sieve/tmp/", tmp_fname, ".sieve", NULL);
+		if (stat(path, &st) < 0 && errno == ENOENT) {
+			/* NICE! doesn't exist */
+			fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
+		  
+			/* Something went wrong */
+			if (fd != -1 || errno != EEXIST)
+			  break;
+		}
+
+		/* Wait and try again - very unlikely */
+		sleep(2);
+		tv = &tv_now;
+		if (gettimeofday(&tv_now, NULL) < 0)
+			i_fatal("gettimeofday(): %m");
+	}
+	
+	*fname = t_strdup(path);
+	if (fd == -1)
+		sieve_storage_set_error(storage, "Call to open() failed");
+
+	pool_unref(pool);
+	return fd;
+}
+
+static int sieve_script_move(struct sieve_save_context *ctx,
+  const char *dst)
+{
+	int failed;
+
+	t_push();
+
+	/* Using rename() to ensure existing files are replaced
+	 * without conflicts with other processes using the same
+	 * file. The kernel wont fully delete the original until
+	 * all processes have closed the file.
+	 */
+	if (rename(ctx->tmp_path, dst) == 0)
+		failed = FALSE;
+	else {
+		failed = TRUE;
+		if (ENOSPACE(errno)) {
+			sieve_storage_set_error
+			  (ctx->storage, "Not enough disk space");
+		} else {
+			sieve_storage_set_critical
+			  (ctx->storage, "link(%s, %s) failed: %m", ctx->tmp_path, dst);
+		}
+	}
+
+	/* Always destroy temp file */
+	(void)unlink(ctx->tmp_path);
+
+	t_pop();
+	return !failed;
+}
+
+struct sieve_save_context *
+sieve_storage_save_init(struct sieve_storage *storage,
+	const char *scriptname, struct istream *input)
+{
+	struct sieve_save_context *ctx;
+	pool_t pool;
+	struct ostream *output;
+	const char *path;
+
+	pool = pool_alloconly_create("sieve_save_context", 4096);
+	ctx = p_new(pool, struct sieve_save_context, 1);
+	ctx->pool = pool;
+	ctx->storage = storage;
+	ctx->finished = TRUE;
+	ctx->scriptname = scriptname;
+
+	t_push();
+
+	ctx->fd = sieve_create_tmp(storage, scriptname, &path);
+	if (ctx->fd == -1) {
+		ctx->failed = TRUE;
+		t_pop();
+		pool_unref(pool);
+		return NULL;
+	}
+
+	ctx->input = input;
+
+	output = o_stream_create_file(ctx->fd, system_pool, 0, FALSE);
+	/*ctx->output = (storage->flags & SIEVE_STORAGE_FLAG_SAVE_CRLF) != 0 ?
+		o_stream_create_crlf(default_pool, output) :
+		o_stream_create_lf(default_pool, output);
+	o_stream_unref(&output);*/
+	ctx->output = output;
+
+	ctx->tmp_path = p_strdup(pool, path);
+	ctx->failed = FALSE;
+
+	t_pop();	
+	return ctx;
+}
+
+int sieve_storage_save_continue(struct sieve_save_context *ctx)
+{
+	if (o_stream_send_istream(ctx->output, ctx->input) < 0) {
+		sieve_storage_set_critical(ctx->storage,
+			"o_stream_send_istream(%s) failed: %m", ctx->tmp_path);
+		ctx->failed = TRUE;
+		return -1;
+	}
+	return 0;
+}
+
+int sieve_storage_save_finish(struct sieve_save_context *ctx)
+{
+	int output_errno;
+
+	ctx->finished = TRUE;
+	if (ctx->failed && ctx->fd == -1) {
+		/* tmp file creation failed */
+		return -1;
+	}
+
+	t_push();
+	output_errno = ctx->output->stream_errno;
+	o_stream_destroy(&ctx->output);
+
+	if (fsync(ctx->fd) < 0) {
+		sieve_storage_set_critical(ctx->storage,
+					  "fsync(%s) failed: %m", ctx->tmp_path);
+		ctx->failed = TRUE;
+	}
+	if (close(ctx->fd) < 0) {
+		sieve_storage_set_critical(ctx->storage,
+					  "close(%s) failed: %m", ctx->tmp_path);
+		ctx->failed = TRUE;
+	}
+	ctx->fd = -1;
+
+	if (ctx->failed) {
+		/* delete the tmp file */
+		if (unlink(ctx->tmp_path) < 0 && errno != ENOENT) 
+			i_warning("Unlink(%s) failed: %m", ctx->tmp_path);
+
+		errno = output_errno;
+		if (ENOSPACE(errno)) {
+			sieve_storage_set_error(ctx->storage,
+					       "Not enough disk space");
+		} else if (errno != 0) {
+			sieve_storage_set_critical(ctx->storage,
+				"write(%s) failed: %m", ctx->tmp_path);
+		}
+
+		t_pop();
+		return -1;
+	}
+	t_pop();
+
+	return 0;
+}
+
+int sieve_storage_save_commit(struct sieve_save_context *ctx)
+{
+	const char *dest_path;
+	bool failed = FALSE;
+
+	i_assert(ctx->output == NULL);
+	i_assert(ctx->finished);
+
+	t_push();
+
+	dest_path = t_strconcat(ctx->storage->dir, "/sieve/", ctx->scriptname, ".sieve", NULL);
+
+	failed = !sieve_script_move(ctx, dest_path);
+	
+	t_pop();
+
+	/* Always get rid of the pool */
+	pool_unref(ctx->pool);
+
+	return !failed;
+}
+
+void sieve_storage_save_abort(struct sieve_save_context *ctx)
+{
+	ctx->failed = TRUE;
+
+	if (!ctx->finished) 
+		(void)sieve_storage_save_finish(ctx);
+	else
+		(void)unlink(ctx->tmp_path);
+
+	i_assert(ctx->output == NULL);
+
+	pool_unref(ctx->pool);
+}
diff -r -u -N --exclude=Makefile.in old/src/lib-sievestorage/sieve-save.h new/src/lib-sievestorage/sieve-save.h
--- old/src/lib-sievestorage/sieve-save.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-sievestorage/sieve-save.h	2006-07-13 00:12:10.000000000 +0200
@@ -0,0 +1,21 @@
+#ifndef __SIEVE_SAVE_H
+#define __SIEVE_SAVE_H
+
+#include "sieve-storage.h"
+
+struct sieve_save_context;
+
+struct sieve_save_context *
+sieve_storage_save_init(struct sieve_storage *storage,
+	const char *scriptname, struct istream *input);
+
+int sieve_storage_save_continue(struct sieve_save_context *ctx);
+
+int sieve_storage_save_finish(struct sieve_save_context *ctx);
+
+void sieve_storage_save_abort(struct sieve_save_context *ctx);
+
+int sieve_storage_save_commit(struct sieve_save_context *ctx);
+
+#endif
+
diff -r -u -N --exclude=Makefile.in old/src/lib-sievestorage/sieve-script.c new/src/lib-sievestorage/sieve-script.c
--- old/src/lib-sievestorage/sieve-script.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-sievestorage/sieve-script.c	2006-07-13 12:19:33.783025845 +0200
@@ -0,0 +1,306 @@
+#include "lib.h"
+#include "hostpid.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "sieve-storage.h"
+#include "sieve-script.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <time.h>
+#include <fcntl.h>
+
+static int
+sieve_storage_open_fd(struct sieve_storage *storage, const char *path, int *fd)
+{
+	*fd = open(path, O_RDONLY);
+	if (*fd != -1)
+		return 1;
+	if (errno == ENOENT)
+		return 0;
+
+	sieve_storage_set_critical(storage,
+				   "open(%s) failed: %m", path);
+	return -1;
+}
+
+struct istream *
+sieve_storage_script_open(struct sieve_storage *storage, 
+	const char *scriptname, bool *deleted_r, uoff_t *size)
+{
+	const struct stat *statbuf;
+	struct istream *stream;
+	const char *path;
+	int fd = -1;
+
+	*deleted_r = FALSE;
+	*size = 0;
+
+	t_push();
+
+	path = t_strconcat( storage->dir, "/sieve/", scriptname, ".sieve", NULL );
+
+	if (sieve_storage_open_fd(storage, path, &fd) < 0)
+	{
+		t_pop();
+		return NULL;
+	}
+	t_pop();
+
+	if (fd == -1) {
+		*deleted_r = TRUE;
+		return NULL;
+	}
+
+	stream = i_stream_create_file(fd, default_pool,
+				      SIEVE_READ_BLOCK_SIZE, TRUE);
+
+	if ( stream != NULL ) {
+		statbuf = i_stream_stat(stream, 0);
+		*size = statbuf->st_size;
+	}
+
+	return stream;
+}
+
+const char *sieve_storage_file_get_scriptname
+	(struct sieve_storage *storage __attr_unused__, const char *filename)
+{
+	const char *ext;
+
+	ext = strrchr(filename, '.');
+
+	if ( ext == NULL || ext == filename || strncmp(ext,".sieve",6) != 0 ) 
+		return NULL;
+	
+	return t_strdup_until(filename, ext);
+}
+
+const char *sieve_storage_get_active_script(struct sieve_storage *storage)
+{
+	static const int bufsize = 255;
+	const char *active_path, *fname;
+	const char *scriptname;
+	char linkbuf[bufsize];
+	int ret;
+
+	active_path = t_strconcat
+	  ( storage->dir, "/", ".dovecot.sieve", NULL );
+
+	ret = readlink( active_path, linkbuf, bufsize-1);
+
+	linkbuf[ret] = '\0';
+
+	if ( ret < 0 ) {
+		if (errno == EINVAL) {
+			/* Our symlink is no symlink. Report 'no active script'.
+			 * Activating a script will automatically resolve this, so
+			 * there is no need to panic on this one.
+			 */
+			i_warning
+			  ("sieve storage: active script symlink %s is no symlink.",
+			   active_path);
+			return "";
+		}
+
+		if (errno != ENOENT ) {
+			/* We do need to panic otherwise */
+			sieve_storage_set_critical
+			  (storage,
+			   "Performing readlink() on %s failed: %m", active_path);
+			return NULL;
+		}
+
+		return "";
+	}
+
+	fname = strrchr(linkbuf, '/');
+	if ( fname == NULL ) {
+		/* Obviously someone has been playing with our symlink,
+		 * ignore this situation and report 'no active script'.
+		 */
+		i_warning
+		  ("sieve_storage: active script symlink %s is broken (points to %s).", 
+		   active_path, linkbuf);
+		return "";
+	}
+	fname++;	
+
+	scriptname = sieve_storage_file_get_scriptname(storage, fname);
+
+	if ( scriptname == NULL )
+	{
+		/* Obviously someone has been playing with our symlink,
+		 * ignore this situation and report 'no active script'.
+		 */
+		i_warning
+		  ("sieve_storage: active script symlink %s is broken (points to %s).",
+		   active_path, linkbuf);
+		return "";
+	}
+
+	return scriptname;
+}
+
+bool
+sieve_storage_script_delete(struct sieve_storage *storage,
+	const char *scriptname) {
+	const char *aname, *path;
+	int ret;
+
+	t_push();
+
+	aname = sieve_storage_get_active_script(storage);
+
+	if (aname == NULL) { 
+		/* Critical error */
+		t_pop();
+		return FALSE;
+	}
+
+	/* Is the requested script active? */
+	if ( strcmp(scriptname, aname) == 0 ) {
+		sieve_storage_set_error(storage, "Cannot delete the active script.");
+		t_pop();
+		return FALSE;
+	}
+
+	path = t_strconcat
+	  ( storage->dir, "/sieve/", scriptname, ".sieve", NULL );
+
+	ret = unlink(path);
+
+	if ( ret < 0 ) {
+		if ( errno == ENOENT ) {
+			sieve_storage_set_error(storage, "Script does not exist.");
+			t_pop();
+			return FALSE;
+		}
+
+		t_pop();
+		sieve_storage_set_critical(storage, "Performing unlink() failed on %s: %m", path);
+		return FALSE;		
+	}
+
+	t_pop();
+	return TRUE;	
+}
+
+bool
+sieve_storage_script_activate(struct sieve_storage *storage,
+  const char *scriptname, bool *was_active)
+{
+	struct stat st;
+	const char *active_path, *active_path_new, *script_path;
+	struct timeval *tv, tv_now;
+	const char *aname;
+	int ret;
+
+	*was_active = FALSE;
+
+	t_push();
+
+	active_path = t_strconcat
+		( storage->dir, "/", ".dovecot.sieve", NULL );
+
+	/* If the scriptname is empty, no script is to be active */
+	if ( scriptname == NULL || *scriptname == '\0' ) {	
+		/* Delete the symlink, so no script is active */
+		(void)unlink(active_path);
+		t_pop();
+		return TRUE;
+	}
+
+	/* Find out whether there is an active script */
+	aname = sieve_storage_get_active_script(storage);
+
+	if ( aname == NULL ) {
+		/* Critical error */
+		t_pop();
+		return FALSE;
+	}
+		
+	/* Is the requested script already active? */
+	if ( strcmp(scriptname, aname) == 0 ) {
+		/* Nothing to do, just exit successfully */
+		t_pop();
+		*was_active = TRUE;
+		return TRUE;
+	}
+
+	/* Just try to create the symlink first */
+	script_path = t_strconcat
+		( storage->dir, "/sieve/", scriptname, ".sieve", NULL );
+
+	if ( stat(script_path, &st) != 0 ) {
+		sieve_storage_set_error(storage, "Script %s does not exist.", scriptname);
+		t_pop();
+		return FALSE;
+	}
+
+	/* Just try to create the symlink first */
+	script_path = t_strconcat
+		( "sieve/", scriptname, ".sieve", NULL );
+		
+ 	ret = symlink(script_path, active_path);
+
+	if ( ret < 0 && errno == EEXIST ) {
+		/* The symlink already exists, try to replace it */
+		tv = &ioloop_timeval;
+
+		for (;;) {		
+			/* First the new symlink is created with a different filename */
+			active_path_new = t_strdup_printf
+			  ("%s/.dovecot.sieve-new.%s.P%sM%s.%s.sieve",
+			   storage->dir,
+			   dec2str(tv->tv_sec), my_pid,
+			   dec2str(tv->tv_usec), my_hostname);
+
+			ret = symlink(script_path, active_path_new);
+		
+			if ( ret < 0 ) {
+				/* If link exists we try again later */
+				if ( errno == EEXIST ) {
+					/* Wait and try again - very unlikely */
+					sleep(2);
+					tv = &tv_now;
+					if (gettimeofday(&tv_now, NULL) < 0)
+						i_fatal("gettimeofday(): %m");
+					continue;
+				}
+
+				/* Other error, critical */
+				sieve_storage_set_critical
+				  (storage, 
+				   "Creating symlink() %s to %s failed.", 
+				   active_path_new, script_path);
+				t_pop();
+				return FALSE;
+			}
+
+			/* Link created */
+			break;
+		}
+
+		/* Replace the existing link and thus activating the new script */
+		ret = rename(active_path_new, active_path);
+
+		if ( ret < 0 ) {
+			/* Failed; created symlink must be deleted */
+			(void)unlink(active_path_new);
+			sieve_storage_set_critical
+			  (storage,
+			   "Performing rename() %s to %s failed.", 
+			   active_path_new, active_path);
+			t_pop();		
+		}	
+	}
+
+	t_pop();
+	return TRUE;
+}
+
+
diff -r -u -N --exclude=Makefile.in old/src/lib-sievestorage/sieve-script.h new/src/lib-sievestorage/sieve-script.h
--- old/src/lib-sievestorage/sieve-script.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-sievestorage/sieve-script.h	2006-07-13 00:12:10.000000000 +0200
@@ -0,0 +1,26 @@
+#ifndef __SIEVE_FILE_H
+#define __SIEVE_FILE_H
+
+#include "sieve-storage.h"
+
+struct istream *
+sieve_storage_script_open(struct sieve_storage *storage, 
+	const char *scriptname, bool *deleted_r, uoff_t *size);
+
+const char *sieve_storage_file_get_scriptname
+  (struct sieve_storage *storage, const char *filename);
+
+const char *sieve_storage_get_active_script(struct sieve_storage *storage);
+
+bool
+sieve_storage_script_activate(struct sieve_storage *storage,
+  const char *scriptname, bool *was_active);
+
+bool
+sieve_storage_script_delete(struct sieve_storage *storage,
+  const char *scriptname);
+
+
+
+#endif
+
diff -r -u -N --exclude=Makefile.in old/src/lib-sievestorage/sieve-storage.c new/src/lib-sievestorage/sieve-storage.c
--- old/src/lib-sievestorage/sieve-storage.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-sievestorage/sieve-storage.c	2006-07-13 12:26:25.733042046 +0200
@@ -0,0 +1,167 @@
+#include "lib.h"
+#include "ioloop.h"
+#include "mkdir-parents.h"
+#include "sieve-storage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <time.h>
+
+#define CREATE_MODE 0770 /* umask() should limit it more */
+
+#define CRITICAL_MSG \
+  "Internal error occured. Refer to server log for more information."
+#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
+
+struct sieve_storage *sieve_storage_create(const char *data, const char *user)
+{
+	struct stat st;
+	struct sieve_storage *storage;
+	const char *home, *root_dir, *dir, *p, *d;
+
+	root_dir = dir = d = NULL;
+
+	t_push();
+
+	/* FIXME: The following code is copied from the mailstorage sources.
+	 * Getting the script storage directory direcly from the mail environment
+	 * is not always appropriate. Must define proper config options.
+	 */
+
+	/* check if we're in the form of mailformat:data
+	   (eg. maildir:Maildir) */
+	p = data;
+	while (i_isalnum(*p)) p++;
+	
+	if (*p == ':') {
+		d = p+1;
+	} else {
+		d = data;
+	}
+
+	if (d == NULL || *d == '\0') {
+		/* we'll need to figure out the maildir location ourself.
+		   it's either root dir if we've already chroot()ed, or
+		   $HOME/Sieve otherwise */
+		if (access("/sieve-filters", R_OK|W_OK|X_OK) == 0)
+			root_dir = "/";
+		else {
+			home = getenv("HOME");
+			if (home != NULL) {
+				if (access(home, R_OK|W_OK|X_OK) == 0)
+					root_dir = home;
+			}
+		}
+	} else {
+		/* <scriptdir> */
+		p = strchr(d, ':');
+		if (p == NULL)
+			root_dir = d;
+		else {
+			dir = t_strdup_until(d, p);
+ 
+			do {
+				p++;
+				if (strncmp(p, "CONTROL=", 8) == 0)
+					root_dir = t_strcut(p+8, ':');
+				p = strchr(p, ':');
+			} while (p != NULL);
+			
+			if ( root_dir == NULL || *root_dir == '\0' )
+				root_dir = dir;
+		}
+	}
+
+	if (root_dir == NULL) {
+		t_pop();
+		return NULL;
+	}
+	
+	if (stat(root_dir, &st) == 0) {
+		/* Ensure sieve directory structure exists */
+		dir = t_strconcat( root_dir, "/sieve/tmp", NULL );
+		
+		if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
+			t_pop();
+			return NULL;
+		}
+
+		storage = i_new(struct sieve_storage, 1);
+		
+		storage->dir = i_strdup(root_dir);
+		storage->user = i_strdup(user);
+		
+		t_pop();
+		return storage;
+	}
+	
+	t_pop();
+	return NULL;
+}
+
+void sieve_storage_free(struct sieve_storage *storage)
+{
+	i_free(storage->dir);
+	i_free(storage->user);
+	i_free(storage->error);
+	i_free(storage);
+}
+
+void sieve_storage_clear_error(struct sieve_storage *storage)
+{
+	i_free(storage->error);
+	storage->error = NULL;
+}
+
+void sieve_storage_set_error(struct sieve_storage *storage, const char *fmt, ...)
+{
+	va_list va;
+
+	sieve_storage_clear_error(storage);
+
+	if (fmt != NULL) {
+		va_start(va, fmt);
+		storage->error = i_strdup_vprintf(fmt, va);
+		va_end(va);
+	}
+}
+
+void sieve_storage_set_internal_error(struct sieve_storage *storage)
+{
+	struct tm *tm;
+	char str[256];
+
+	tm = localtime(&ioloop_time);
+
+	i_free(storage->error);
+	storage->error =
+	  strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
+	  i_strdup(str) : i_strdup(CRITICAL_MSG);
+}
+
+void sieve_storage_set_critical(struct sieve_storage *storage,
+             const char *fmt, ...)
+{
+	va_list va;
+	
+	sieve_storage_clear_error(storage);
+	if (fmt != NULL) {
+		va_start(va, fmt);
+		i_error("%s", t_strdup_vprintf(fmt, va));
+		va_end(va);
+		
+		/* critical errors may contain sensitive data, so let user
+		   see only "Internal error" with a timestamp to make it
+		   easier to look from log files the actual error message. */
+		sieve_storage_set_internal_error(storage);
+	}
+}
+
+const char *sieve_storage_get_last_error(struct sieve_storage *storage)
+{
+	return storage->error;
+}
+
diff -r -u -N --exclude=Makefile.in old/src/lib-sievestorage/sieve-storage.h new/src/lib-sievestorage/sieve-storage.h
--- old/src/lib-sievestorage/sieve-storage.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/lib-sievestorage/sieve-storage.h	2006-07-13 00:12:10.000000000 +0200
@@ -0,0 +1,39 @@
+#ifndef __SIEVE_STORAGE_H
+#define __SIEVE_STORAGE_H
+
+enum sieve_storage_flags {
+  /* Print debugging information while initializing the storage */
+  SIEVE_STORAGE_FLAG_DEBUG     = 0x01,
+  /* Use CRLF linefeeds when saving mails. */
+  SIEVE_STORAGE_FLAG_SAVE_CRLF   = 0x02,
+};
+
+#define SIEVE_READ_BLOCK_SIZE (1024*8)
+
+/* All methods returning int return either TRUE or FALSE. */
+struct sieve_storage {
+	char *name;
+
+	char *dir; 
+
+	char *user; /* name of user accessing the storage */
+	char *error;
+
+	enum sieve_storage_flags flags;
+};
+
+struct sieve_storage *sieve_storage_create(const char *data, const char *user);
+void sieve_storage_free(struct sieve_storage *storage);
+
+/* Set error message in storage. Critical errors are logged with i_error(),
+   but user sees only "internal error" message. */
+void sieve_storage_clear_error(struct sieve_storage *storage);
+void sieve_storage_set_error(struct sieve_storage *storage,
+          const char *fmt, ...) __attr_format__(2, 3);
+void sieve_storage_set_critical(struct sieve_storage *storage,
+             const char *fmt, ...) __attr_format__(2, 3);
+void sieve_storage_set_internal_error(struct sieve_storage *storage);
+
+const char *sieve_storage_get_last_error(struct sieve_storage *storage);
+
+#endif
diff -r -u -N --exclude=Makefile.in old/src/Makefile.am new/src/Makefile.am
--- old/src/Makefile.am	2006-06-11 10:42:50.000000000 +0200
+++ new/src/Makefile.am	2006-07-13 00:22:32.000000000 +0200
@@ -6,6 +6,8 @@
 DELIVER = deliver
 endif
 
+MANAGESIEVE = lib-managesieve lib-sievestorage managesieve managesieve-login
+
 SUBDIRS = \
 	lib \
 	lib-dict \
@@ -26,5 +28,6 @@
 	imap \
 	$(POP3D) \
 	$(DELIVER) \
+	$(MANAGESIEVE) \
 	util \
 	plugins
diff -r -u -N --exclude=Makefile.in old/src/managesieve/client.c new/src/managesieve/client.c
--- old/src/managesieve/client.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/client.c	2006-07-13 11:40:27.342749956 +0200
@@ -0,0 +1,510 @@
+#include "common.h"
+#include "str.h"
+#include "ioloop.h"
+#include "network.h"
+#include "istream.h"
+#include "ostream.h"
+#include "commands.h"
+#include "managesieve-quote.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+static struct client *my_client; /* we don't need more than one currently */
+static struct timeout *to_idle;
+
+struct client *client_create(int fd_in, int fd_out, struct sieve_storage *storage)
+{
+	struct client *client;
+
+	/* always use nonblocking I/O */
+	net_set_nonblock(fd_in, TRUE);
+	net_set_nonblock(fd_out, TRUE);
+
+	client = i_new(struct client, 1);
+	client->fd_in = fd_in;
+	client->fd_out = fd_out;
+	client->input = i_stream_create_file(fd_in, default_pool,
+					     managesieve_max_line_length, FALSE);
+	client->output = o_stream_create_file(fd_out, default_pool,
+					      (size_t)-1, FALSE);
+
+	o_stream_set_flush_callback(client->output, _client_output, client);
+
+	client->io = io_add(fd_in, IO_READ, _client_input, client);
+	client->parser = managesieve_parser_create(client->input, client->output,
+					    managesieve_max_line_length);
+	client->last_input = ioloop_time;
+
+	client->cmd.pool = pool_alloconly_create("command pool", 8192);
+	client->cmd.client = client;
+
+	client->storage = storage;
+
+	i_assert(my_client == NULL);
+	my_client = client;
+
+	if (hook_client_created != NULL)
+		hook_client_created(&client);
+	return client;
+}
+
+void client_destroy(struct client *client, const char *reason)
+{
+	int ret;
+
+	i_assert(!client->destroyed);
+	client->destroyed = TRUE;
+
+	if (!client->disconnected) {
+		if (reason == NULL)
+			reason = "Disconnected";
+		i_info("%s", reason);
+	}
+
+	if (client->command_pending) {
+		/* try to deinitialize the command */
+		i_assert(client->cmd.func != NULL);
+		i_stream_close(client->input);
+		o_stream_close(client->output);
+		client->input_pending = FALSE;
+
+		ret = client->cmd.func(&client->cmd);
+		i_assert(ret);
+	}
+
+	managesieve_parser_destroy(&client->parser);
+	if (client->io != NULL)
+		io_remove(&client->io);
+
+	i_stream_destroy(&client->input);
+	o_stream_destroy(&client->output);
+
+	if (close(client->fd_in) < 0)
+		i_error("close(client in) failed: %m");
+	if (client->fd_in != client->fd_out) {
+		if (close(client->fd_out) < 0)
+			i_error("close(client out) failed: %m");
+	}
+
+	pool_unref(client->cmd.pool);
+	i_free(client);
+
+	/* quit the program */
+	my_client = NULL;
+	io_loop_stop(ioloop);
+}
+
+void client_disconnect(struct client *client, const char *reason)
+{
+	i_assert(reason != NULL);
+
+	if (client->disconnected)
+		return;
+
+	i_info("Disconnected: %s", reason);
+	client->disconnected = TRUE;
+	(void)o_stream_flush(client->output);
+
+	i_stream_close(client->input);
+	o_stream_close(client->output);
+}
+
+int _client_send_line(struct client *client, const char *data) 
+{
+	struct const_iovec iov[2];
+
+	if (client->output->closed)
+		return -1;
+
+	iov[0].iov_base = data;
+	iov[0].iov_len = strlen(data);
+	iov[1].iov_base = "\r\n";
+	iov[1].iov_len = 2;
+
+	if (o_stream_sendv(client->output, iov, 2) < 0)
+		return -1;
+
+	if (o_stream_get_buffer_used_size(client->output) >=
+	    CLIENT_OUTPUT_OPTIMAL_SIZE) {
+		/* buffer full, try flushing */
+		return o_stream_flush(client->output);
+	}
+	return 1;
+}
+
+int client_send_line(struct client_command_context *cmd, const char *data)
+{
+  struct client *client = cmd->client;
+  
+	return _client_send_line(client, data);
+}
+
+void _client_send_response(struct client_command_context *cmd,
+  const char *oknobye, const char *resp_code, const char *msg)
+{
+	string_t *str;
+	
+	str = t_str_new(128);
+	str_append(str, oknobye);
+
+	if ( resp_code != NULL ) {
+		str_append(str, " (");
+		str_append(str, resp_code);
+		str_append_c(str, ')');
+	}
+
+	if ( msg != NULL ) {
+		str_append_c(str, ' ');
+		managesieve_quote_append_string(str, msg, TRUE);
+	}
+
+	_client_send_line(cmd->client, str_c(str));
+}
+
+void client_disconnect_with_error(struct client *client, const char *msg)
+{
+	_client_send_line(client, t_strconcat("BYE \"", msg, "\"", NULL));
+	client_disconnect(client, msg);
+}
+
+void client_send_command_error(struct client_command_context *cmd,
+			       const char *msg)
+{
+	struct client *client = cmd->client;
+	const char *error, *cmd_name;
+	bool fatal;
+
+	if (msg == NULL) {
+		msg = managesieve_parser_get_error(client->parser, &fatal);
+		if (fatal) {
+			client_disconnect_with_error(client, msg);
+			return;
+		}
+	}
+
+	if (cmd->name == NULL)
+		error = t_strconcat
+			("Error in MANAGESIEVE command: ", msg, NULL);
+	else {
+		cmd_name = t_str_ucase(cmd->name);
+		error = t_strconcat
+			("Error in MANAGESIEVE command ", cmd_name, ": ", msg, NULL);
+	}
+
+	client_send_no(cmd, error);
+
+	if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
+		client_disconnect_with_error(client,
+			"Too many invalid MANAGESIEVE commands.");
+	}
+
+	/* client_read_args() failures rely on this being set, so that the
+	   command processing is stopped even while command function returns
+	   FALSE. */
+	cmd->param_error = TRUE;
+}
+
+bool client_read_args(struct client_command_context *cmd, unsigned int count,
+		      unsigned int flags, struct managesieve_arg **args)
+{
+	int ret;
+
+	i_assert(count <= INT_MAX);
+
+	ret = managesieve_parser_read_args(cmd->client->parser, count, flags, args);
+	if (ret >= (int)count) {
+		/* all parameters read successfully */
+		return TRUE;
+	} else if (ret == -2) {
+		/* need more data */
+		return FALSE;
+	} else {
+		/* error, or missing arguments */
+		client_send_command_error(cmd, ret < 0 ? NULL :
+					  "Missing arguments");
+		return FALSE;
+	}
+}
+
+bool client_read_string_args(struct client_command_context *cmd,
+			     unsigned int count, ...)
+{
+	struct managesieve_arg *managesieve_args;
+	va_list va;
+	const char *str;
+	unsigned int i;
+
+	if (!client_read_args(cmd, count, 0, &managesieve_args))
+		return FALSE;
+
+	va_start(va, count);
+	for (i = 0; i < count; i++) {
+		const char **ret = va_arg(va, const char **);
+
+		if (managesieve_args[i].type == MANAGESIEVE_ARG_EOL) {
+			client_send_command_error(cmd, "Missing arguments.");
+			break;
+		}
+
+		str = managesieve_arg_string(&managesieve_args[i]);
+		if (str == NULL) {
+			client_send_command_error(cmd, "Invalid arguments.");
+			break;
+		}
+
+		if (ret != NULL)
+			*ret = str;
+	}
+	va_end(va);
+
+	return i == count;
+}
+
+void _client_reset_command(struct client *client)
+{
+	pool_t pool;
+	size_t size;
+
+	/* reset input idle time because command output might have taken a
+	   long time and we don't want to disconnect client immediately then */
+	client->last_input = ioloop_time;
+
+	client->command_pending = FALSE;
+	if (client->io == NULL) {
+		client->io = io_add(i_stream_get_fd(client->input),
+				    IO_READ, _client_input, client);
+	}
+	o_stream_set_flush_callback(client->output, _client_output, client);
+
+	pool = client->cmd.pool;
+	memset(&client->cmd, 0, sizeof(client->cmd));
+
+	p_clear(pool);
+	client->cmd.pool = pool;
+	client->cmd.client = client;
+
+	managesieve_parser_reset(client->parser);
+
+	/* if there's unread data in buffer, remember that there's input
+	   pending and we should get around to calling client_input() soon.
+	   This is mostly for APPEND/IDLE. */
+	(void)i_stream_get_data(client->input, &size);
+	if (size > 0 && !client->destroyed)
+		client->input_pending = TRUE;
+}
+
+/* Skip incoming data until newline is found,
+   returns TRUE if newline was found. */
+static bool client_skip_line(struct client *client)
+{
+	const unsigned char *data;
+	size_t i, data_size;
+
+	data = i_stream_get_data(client->input, &data_size);
+
+	for (i = 0; i < data_size; i++) {
+		if (data[i] == '\n') {
+			client->input_skip_line = FALSE;
+			i++;
+			break;
+		}
+	}
+
+	i_stream_skip(client->input, i);
+	return !client->input_skip_line;
+}
+
+static bool client_handle_input(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+
+	if (cmd->func != NULL) {
+		/* command is being executed - continue it */
+		if (cmd->func(cmd) || cmd->param_error) {
+			/* command execution was finished */
+			client->bad_counter = 0;
+			_client_reset_command(client);
+			return TRUE;
+		}
+		return FALSE;
+	}
+
+	if (client->input_skip_line) {
+		/* we're just waiting for new line.. */
+		if (!client_skip_line(client))
+			return FALSE;
+
+		/* got the newline */
+		_client_reset_command(client);
+
+		/* pass through to parse next command */
+	}
+
+	if (cmd->name == NULL) {
+		cmd->name = managesieve_parser_read_word(client->parser);
+		if (cmd->name == NULL)
+			return FALSE; /* need more data */
+		cmd->name = p_strdup(cmd->pool, cmd->name);
+	}
+
+	if (cmd->name == '\0') {
+		/* command not given - cmd_func is already NULL. */
+	} else {
+		/* find the command function */
+		cmd->func = command_find(cmd->name);
+	}
+
+	if (cmd->func == NULL) {
+		/* unknown command */
+		client_send_command_error(cmd, "Unknown command.");
+		client->input_skip_line = TRUE;
+		_client_reset_command(client);
+	} else {
+		client->input_skip_line = TRUE;
+		if (cmd->func(cmd) || cmd->param_error) {
+			/* command execution was finished. */
+			client->bad_counter = 0;
+			_client_reset_command(client);
+		} else {
+			/* unfinished */
+			if (client->command_pending) {
+				o_stream_set_flush_pending(client->output,
+							   TRUE);
+			}
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+void _client_input(void *context)
+{
+	struct client *client = context;
+	struct client_command_context *cmd = &client->cmd;
+	int ret;
+
+	if (client->command_pending) {
+		/* already processing one command. wait. */
+		io_remove(&client->io);
+		return;
+	}
+
+	client->input_pending = FALSE;
+	client->last_input = ioloop_time;
+
+	switch (i_stream_read(client->input)) {
+	case -1:
+		/* disconnected */
+		client_destroy(client, NULL);
+		return;
+	case -2:
+		/* parameter word is longer than max. input buffer size.
+		   this is most likely an error, so skip the new data
+		   until newline is found. */
+		client->input_skip_line = TRUE;
+
+		client_send_command_error(cmd, "Too long argument.");
+		_client_reset_command(client);
+		break;
+	}
+
+	o_stream_cork(client->output);
+	do {
+		t_push();
+		ret = client_handle_input(cmd);
+		t_pop();
+	} while (ret);
+	o_stream_uncork(client->output);
+
+	if (client->command_pending)
+		client->input_pending = TRUE;
+
+	if (client->output->closed)
+		client_destroy(client, NULL);
+}
+
+int _client_output(void *context)
+{
+	struct client *client = context;
+	struct client_command_context *cmd = &client->cmd;
+	int ret;
+	bool finished;
+
+	client->last_output = ioloop_time;
+
+	if ((ret = o_stream_flush(client->output)) < 0) {
+		client_destroy(client, NULL);
+		return 1;
+	}
+
+	if (!client->command_pending)
+		return 1;
+
+	/* continue processing command */
+	o_stream_cork(client->output);
+	client->output_pending = TRUE;
+	finished = cmd->func(cmd) || cmd->param_error;
+
+	/* a bit kludgy check. normally we would want to get back to this
+	   output handler, but IDLE is a special case which has command
+	   pending but without necessarily anything to write. */
+	if (!finished && client->output_pending)
+		o_stream_set_flush_pending(client->output, TRUE);
+
+	o_stream_uncork(client->output);
+
+	if (finished) {
+		/* command execution was finished */
+		client->bad_counter = 0;
+		_client_reset_command(client);
+
+		if (client->input_pending)
+			_client_input(client);
+	}
+	return ret;
+}
+
+static void idle_timeout(void *context __attr_unused__)
+{
+	time_t idle_time;
+
+	if (my_client == NULL)
+		return;
+
+	idle_time = ioloop_time -
+		I_MAX(my_client->last_input, my_client->last_output);
+
+	if (my_client->command_pending &&
+	    o_stream_get_buffer_used_size(my_client->output) > 0 &&
+	    idle_time >= CLIENT_OUTPUT_TIMEOUT) {
+		/* client isn't reading our output */
+		client_destroy(my_client, "Disconnected for inactivity "
+			       "in reading our output");
+	} else if (idle_time >= CLIENT_IDLE_TIMEOUT) {
+		/* client isn't sending us anything */
+		if (!my_client->command_pending) {
+			_client_send_line(my_client,
+					 "BYE \"Disconnected for inactivity.\"");
+		}
+		client_destroy(my_client, "Disconnected for inactivity");
+	}
+}
+
+void clients_init(void)
+{
+	my_client = NULL;
+	to_idle = timeout_add(10000, idle_timeout, NULL);
+}
+
+void clients_deinit(void)
+{
+	if (my_client != NULL) {
+		_client_send_line(my_client, "BYE \"Server shutting down.\"");
+		client_destroy(my_client, "Server shutting down");
+	}
+
+	timeout_remove(&to_idle);
+}
+
diff -r -u -N --exclude=Makefile.in old/src/managesieve/client.h new/src/managesieve/client.h
--- old/src/managesieve/client.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/client.h	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,98 @@
+#ifndef __CLIENT_H
+#define __CLIENT_H
+
+#include "commands.h"
+
+struct client;
+struct sieve_storage;
+struct managesieve_parser;
+struct managesieve_arg;
+
+struct client_command_context {
+	struct client *client;
+
+	pool_t pool;
+	const char *name;
+
+	command_func_t *func;
+	void *context;
+
+	unsigned int param_error:1;
+};
+
+struct client {
+	int fd_in, fd_out;
+	struct sieve_storage *storage;
+
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+
+	time_t last_input, last_output;
+	unsigned int bad_counter;
+
+	struct managesieve_parser *parser;
+	struct client_command_context cmd;
+
+	unsigned int disconnected:1;
+	unsigned int destroyed:1;
+	unsigned int command_pending:1;
+	unsigned int input_pending:1;
+	unsigned int output_pending:1;
+	unsigned int rawlog:1;
+	unsigned int input_skip_line:1; /* skip all the data until we've
+					   found a new line */
+};
+
+/* Create new client with specified input/output handles. socket specifies
+   if the handle is a socket. */
+struct client *client_create(int fd_in, int fd_out, struct sieve_storage *storage);
+void client_destroy(struct client *client, const char *reason);
+
+/* Disconnect client connection */
+void client_disconnect(struct client *client, const char *reason);
+void client_disconnect_with_error(struct client *client, const char *msg);
+
+/* Send a line of data to client. Returns 1 if ok, 0 if buffer is getting full,
+   -1 if error */
+int client_send_line(struct client_command_context *cmd, const char *data);
+int _client_send_line(struct client *client, const char *data);
+
+void _client_send_response(struct client_command_context *cmd,
+  const char *oknobye, const char *resp_code, const char *msg);
+
+#define client_send_ok(cmd, msg) \
+  _client_send_response(cmd, "OK", NULL, msg)
+#define client_send_no(cmd, msg) \
+  _client_send_response(cmd, "NO", NULL, msg)
+#define client_send_bye(cmd, msg) \
+  _client_send_response(cmd, "BYE", NULL, msg)
+
+#define client_send_okresp(cmd, resp_code, msg) \
+  _client_send_response(cmd, "OK", resp_code, msg)
+#define client_send_noresp(cmd, resp_code, msg) \
+  _client_send_response(cmd, "NO", resp_code, msg)
+#define client_send_byeresp(cmd, resp_code, msg) \
+  _client_send_response(cmd, "BYE", resp_code, msg)
+
+/* Send BAD command error to client. msg can be NULL. */
+void client_send_command_error(struct client_command_context *cmd,
+			       const char *msg);
+
+/* Read a number of arguments. Returns TRUE if everything was read or
+   FALSE if either needs more data or error occurred. */
+bool client_read_args(struct client_command_context *cmd, unsigned int count,
+		      unsigned int flags, struct managesieve_arg **args);
+/* Reads a number of string arguments. ... is a list of pointers where to
+   store the arguments. */
+bool client_read_string_args(struct client_command_context *cmd,
+			     unsigned int count, ...);
+
+void clients_init(void);
+void clients_deinit(void);
+
+void _client_reset_command(struct client *client);
+void _client_input(void *context);
+int _client_output(void *context);
+
+#endif
diff -r -u -N --exclude=Makefile.in old/src/managesieve/cmd-capability.c new/src/managesieve/cmd-capability.c
--- old/src/managesieve/cmd-capability.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/cmd-capability.c	2006-07-13 11:42:18.632273163 +0200
@@ -0,0 +1,13 @@
+#include "common.h"
+#include "commands.h"
+#include "str.h"
+
+bool cmd_capability(struct client_command_context *cmd)
+{
+	client_send_line(cmd, "\"IMPLEMENTATION\" \"" PACKAGE "\"");
+	client_send_line(cmd, "\"SIEVE\" \"FILEINTO VACATION\"");
+	client_send_line(cmd, "OK \"Capability completed.\"");
+	return TRUE;
+
+}
+
diff -r -u -N --exclude=Makefile.in old/src/managesieve/cmd-deletescript.c new/src/managesieve/cmd-deletescript.c
--- old/src/managesieve/cmd-deletescript.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/cmd-deletescript.c	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,21 @@
+#include "common.h"
+#include "commands.h"
+
+#include "sieve-script.h"
+
+bool cmd_deletescript(struct client_command_context *cmd)
+{
+	struct sieve_storage *storage = cmd->client->storage;
+	const char *scriptname;
+
+	/* <scrip name>*/
+	if (!client_read_string_args(cmd, 1, &scriptname))
+		return FALSE;
+
+	if (!sieve_storage_script_delete(storage, scriptname))
+		client_send_storage_error(cmd, storage);
+	else
+		client_send_ok(cmd, "Deletescript completed.");
+
+	return TRUE;
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve/cmd-getscript.c new/src/managesieve/cmd-getscript.c
--- old/src/managesieve/cmd-getscript.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/cmd-getscript.c	2006-07-13 11:53:39.437618845 +0200
@@ -0,0 +1,111 @@
+#include "common.h"
+#include "ostream.h"
+#include "commands.h"
+#include "istream.h"
+#include "sieve-script.h"
+
+struct cmd_getscript_context {
+	struct client *client;
+	struct client_command_context *cmd;
+	struct sieve_storage *storage;	
+	const char *scriptname;	
+	uoff_t scriptsize;
+
+	struct istream *filestream;
+	struct istream *scriptstream;
+	bool failed;
+	bool deleted_r;
+};
+
+static bool cmd_getscript_finish(struct cmd_getscript_context *ctx)
+{
+	struct client_command_context *cmd = ctx->cmd;
+
+	if (ctx->scriptstream != NULL)
+		i_stream_unref(&(ctx->scriptstream));
+
+	if (ctx->failed) {
+		if (cmd->client->output->closed) {
+			client_disconnect(cmd->client, "Disconnected");
+			return TRUE;
+		}
+
+		if (ctx->deleted_r) {
+			client_send_no(cmd, "Script does not exist.");
+			return TRUE;
+		}
+		
+		client_send_storage_error(cmd, cmd->client->storage);
+		return TRUE;
+	}
+
+	client_send_line(cmd, "");
+	client_send_ok(cmd, "Getscript completed.");
+	return TRUE;
+}
+
+static bool cmd_getscript_continue(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+	struct cmd_getscript_context *ctx = cmd->context;
+
+	if (o_stream_send_istream(client->output, ctx->scriptstream) < 0) {
+		sieve_storage_set_critical(ctx->storage,
+			"o_stream_send_istream(%s) failed: %m", ctx->scriptname);
+		ctx->failed = TRUE;    
+	}
+
+	/* FIXME: Check whether there is a bug in the io_stream_sendfile function
+	 * as the eof indicator of the input stream is never set. The stream_sendfile
+	 * function does not use read functions of the inputstream and therefore
+	 * the eof indicator will not be updated. Workaround: check v_offset == size 
+	 * as well.
+	 */
+	if (ctx->scriptstream->eof || ctx->scriptstream->closed ||
+		ctx->scriptstream->v_offset == ctx->scriptsize ) {
+		if (client->output->closed || ctx->scriptstream->v_offset < ctx->scriptsize) 
+			ctx->failed = TRUE;
+	} else if (!ctx->failed) 
+		/* unfinished */
+		return FALSE;
+
+	return cmd_getscript_finish(ctx);
+}
+
+bool cmd_getscript(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+	struct cmd_getscript_context *ctx;
+	const char *scriptname;
+	uoff_t scriptsize;
+
+	/* <scriptname> */
+	if (!client_read_string_args(cmd, 1, &scriptname))
+		return FALSE;
+
+	ctx = p_new(cmd->pool, struct cmd_getscript_context, 1);
+	ctx->cmd = cmd;
+	ctx->client = client;
+	ctx->storage = client->storage;
+	ctx->scriptname = scriptname;
+	ctx->failed = FALSE;
+	ctx->deleted_r = FALSE;
+	
+	ctx->scriptstream = sieve_storage_script_open(client->storage,
+		scriptname, &(ctx->deleted_r), &scriptsize);
+
+	if ( ctx->scriptstream == NULL ) {
+		ctx->failed = TRUE;
+		return cmd_getscript_finish(ctx);
+	}
+
+	ctx->scriptsize = scriptsize;
+
+	client_send_line(cmd, t_strdup_printf("{%"PRIuUOFF_T"+}", scriptsize));
+
+	client->command_pending = TRUE;
+	cmd->func = cmd_getscript_continue;
+	cmd->context = ctx;
+
+	return cmd_getscript_continue(cmd);
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve/cmd-havespace.c new/src/managesieve/cmd-havespace.c
--- old/src/managesieve/cmd-havespace.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/cmd-havespace.c	2006-07-13 11:54:33.118357086 +0200
@@ -0,0 +1,37 @@
+#include "common.h"
+#include "commands.h"
+
+bool cmd_havespace(struct client_command_context *cmd)
+{
+	struct managesieve_arg *args;
+	const char *scriptname;
+	uoff_t size;
+	int ret;
+
+	/* <scriptname> <size> */
+	if (!(ret=client_read_args(cmd, 2, 0, &args)))
+	  return FALSE;
+
+	if ( ret > 2 ) {
+		client_send_no(cmd, "Too many arguments");
+		return TRUE;
+	}
+
+	if ( (scriptname = managesieve_arg_string(&args[0])) == NULL ) {
+		client_send_no(cmd, "Invalid string for scriptname.");
+		return TRUE;
+	}
+
+	if ( managesieve_arg_number(&args[1], &size) < 0 ) {
+		client_send_no(cmd, "Invalid scriptsize argument.");
+		return TRUE;
+	}
+
+	if ( size == 0 ) {
+		client_send_no(cmd, "Cannot upload empty script.");
+		return TRUE;
+	}
+
+	client_send_ok(cmd, "Putscript would succeed.");
+	return TRUE;
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve/cmd-listscripts.c new/src/managesieve/cmd-listscripts.c
--- old/src/managesieve/cmd-listscripts.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/cmd-listscripts.c	2006-07-13 11:55:58.619533110 +0200
@@ -0,0 +1,45 @@
+#include "common.h"
+#include "commands.h"
+#include "str.h"
+#include "sieve-storage.h"
+#include "sieve-list.h"
+#include "managesieve-quote.h"
+
+bool cmd_listscripts(struct client_command_context *cmd)
+{
+	struct sieve_list_context *ctx;
+	const char *scriptname;
+	bool active;
+	string_t *str;
+
+	if ( (ctx = sieve_storage_list_init(cmd->client->storage))
+		== NULL ) {
+		client_send_storage_error(cmd, cmd->client->storage);
+		return TRUE;
+	}
+
+	/* FIXME: This will be quite slow for large script lists. Implement
+	 * some buffering to fix this. Wont truely be an issue with managesieve
+	 * though.
+	 */
+	while ((scriptname = sieve_storage_list_next(ctx, &active)) != NULL) {
+		t_push();
+		str = t_str_new(128);
+	  
+		managesieve_quote_append_string(str, scriptname, FALSE);
+		
+		if ( active ) 
+		  str_append(str, " ACTIVE");
+		
+		client_send_line(cmd, str_c(str));
+		t_pop();
+	}
+  
+	if ( sieve_storage_list_deinit(&ctx) < 0 ) {
+		client_send_storage_error(cmd, cmd->client->storage);
+		return TRUE;
+	}
+	
+	client_send_ok(cmd, "Listscripts completed.");
+	return TRUE;
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve/cmd-logout.c new/src/managesieve/cmd-logout.c
--- old/src/managesieve/cmd-logout.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/cmd-logout.c	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,12 @@
+#include "common.h"
+#include "ostream.h"
+#include "commands.h"
+
+bool cmd_logout(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+
+	client_send_line(cmd, "OK \"Logout completed.\"");
+	client_disconnect(client, "Logged out");
+	return TRUE;
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve/cmd-putscript.c new/src/managesieve/cmd-putscript.c
--- old/src/managesieve/cmd-putscript.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/cmd-putscript.c	2006-07-13 11:59:14.262224870 +0200
@@ -0,0 +1,353 @@
+#include "common.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "str.h"
+#include "commands.h"
+#include "managesieve-parser.h"
+#include "sieve-storage.h"
+#include "sieve-save.h"
+
+#include <sys/time.h>
+
+struct cmd_putscript_context {
+	struct client *client;
+	struct client_command_context *cmd;
+	struct sieve_storage *storage;
+
+	struct istream *input;
+
+	const char *scriptname;
+	uoff_t script_size;	
+
+	struct managesieve_parser *save_parser;
+	struct sieve_save_context *save_ctx;
+};
+
+static void cmd_putscript_finish(struct cmd_putscript_context *ctx);
+static bool cmd_putscript_continue_message(struct client_command_context *cmd);
+
+static void client_input(void *context)
+{
+	struct client *client = context;
+	struct client_command_context *cmd = &client->cmd;
+
+	client->last_input = ioloop_time;
+
+	switch (i_stream_read(client->input)) {
+	case -1:
+		/* disconnected */
+		cmd_putscript_finish(cmd->context);
+		/* Reset command so that client_destroy() doesn't try to call
+		   cmd_putscript_continue_message() anymore. */
+		_client_reset_command(client);
+		client_destroy(client, "Disconnected in putscript");
+		return;
+	case -2:
+		cmd_putscript_finish(cmd->context);
+		if (client->command_pending) {
+			/* message data, this is handled internally by
+			   mailbox_save_continue() */
+			break;
+		}
+
+		/* parameter word is longer than max. input buffer size.
+		   this is most likely an error, so skip the new data
+		   until newline is found. */
+		client->input_skip_line = TRUE;
+
+		client_send_command_error(cmd, "Too long argument.");
+		_client_reset_command(client);
+		return;
+	}
+
+	if (cmd->func(cmd)) {
+		/* command execution was finished. Note that if cmd_sync()
+		   didn't finish, we didn't get here but the input handler
+		   has already been moved. So don't do anything important
+		   here..
+
+		   reset command once again to reset cmd_sync()'s changes. */
+		_client_reset_command(client);
+
+		if (client->input_pending)
+			_client_input(client);
+	}
+}
+
+/* Returns -1 = error, 0 = need more data, 1 = successful. flags and
+   internal_date may be NULL as a result, but mailbox and msg_size are always
+   set when successful. */
+static int validate_args(struct managesieve_arg *args,
+       uoff_t *script_size,
+			 bool *nonsync)
+{
+	if (args->type != MANAGESIEVE_ARG_LITERAL_SIZE &&
+	    args->type != MANAGESIEVE_ARG_LITERAL_SIZE_NONSYNC) {
+		*nonsync = FALSE;
+		return FALSE;
+	}
+
+	*nonsync = args->type == MANAGESIEVE_ARG_LITERAL_SIZE_NONSYNC;
+	*script_size = MANAGESIEVE_ARG_LITERAL_SIZE(args);
+	return TRUE;
+}
+
+static void cmd_putscript_finish(struct cmd_putscript_context *ctx)
+{
+	io_remove(&ctx->client->io);
+
+	managesieve_parser_destroy(&ctx->save_parser);
+
+	if (ctx->input != NULL)
+		i_stream_unref(&ctx->input);
+
+	if (ctx->save_ctx != NULL)
+	{
+		ctx->client->input_skip_line = TRUE;
+		sieve_storage_save_abort(ctx->save_ctx);
+	}
+
+	ctx->client->bad_counter = 0;
+	o_stream_set_flush_callback(ctx->client->output,
+				    _client_output, ctx->client);
+}
+
+static bool cmd_putscript_continue_cancel(struct client_command_context *cmd)
+{
+	struct cmd_putscript_context *ctx = cmd->context;
+	size_t size;
+
+	(void)i_stream_read(ctx->input);
+	(void)i_stream_get_data(ctx->input, &size);
+	i_stream_skip(ctx->input, size);
+
+	if (ctx->input->v_offset == ctx->script_size ||
+	    cmd->client->input->closed) {
+		cmd_putscript_finish(ctx);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static bool cmd_putscript_cancel(struct cmd_putscript_context *ctx, bool nonsync)
+{
+	if (!nonsync) {
+		cmd_putscript_finish(ctx);
+		return TRUE;
+	}
+
+	/* we have to read the nonsynced literal so we don't treat the message
+	   data as commands. */
+	ctx->input = i_stream_create_limit(default_pool, ctx->client->input,
+					   ctx->client->input->v_offset,
+					   ctx->script_size);
+
+	ctx->client->command_pending = TRUE;
+	ctx->cmd->func = cmd_putscript_continue_cancel;
+	ctx->cmd->context = ctx;
+	return cmd_putscript_continue_cancel(ctx->cmd);
+}
+
+static bool cmd_putscript_finish_parsing(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+	struct cmd_putscript_context *ctx = cmd->context;
+	struct managesieve_arg *args;
+	int ret;
+	
+	/* if error occurs, the CRLF is already read. */
+	client->input_skip_line = FALSE;
+	
+	/* <script literal> */
+	ret = managesieve_parser_read_args(ctx->save_parser, 0,
+          MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE, &args);
+
+	if (ret == -1) {
+		if (ctx->storage != NULL)
+			client_send_command_error(cmd, NULL);
+		cmd_putscript_finish(ctx);
+		return TRUE;
+	}
+	if (ret < 0) {
+		/* need more data */
+		return FALSE;
+	}
+
+	if (args[0].type == MANAGESIEVE_ARG_EOL) {
+		/* last message */
+	  
+	  	/* eat away the trailing CRLF */
+		client->input_skip_line = TRUE;
+	  
+		ret = sieve_storage_save_commit(ctx->save_ctx);
+		if (ret < 0) {
+			client_send_storage_error(cmd, ctx->storage);
+			cmd_putscript_finish(ctx);
+			return TRUE;
+		} else 
+			ctx->save_ctx = NULL;
+	  
+		cmd_putscript_finish(ctx);
+		client_send_ok(cmd, "Putscript completed.");
+		
+		return TRUE;
+	}
+
+	client_send_command_error(cmd, "Too many command arguments.");
+	cmd_putscript_finish(ctx);
+	return TRUE;
+}
+
+static bool cmd_putscript_continue_parsing(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+	struct cmd_putscript_context *ctx = cmd->context;
+	struct managesieve_arg *args;
+	int ret;
+	bool nonsync;
+
+	/* if error occurs, the CRLF is already read. */
+	client->input_skip_line = FALSE;
+
+	/* <script literal> */
+	ret = managesieve_parser_read_args(ctx->save_parser, 0,
+				    MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE, &args);
+	if (ret == -1) {
+		cmd_putscript_finish(ctx);
+		return TRUE;
+	}
+	if (ret < 0) {
+		/* need more data */
+		return FALSE;
+	}
+
+	if (!validate_args(args, &ctx->script_size, &nonsync)) {
+		client_send_command_error(cmd, "Invalid arguments.");
+		return cmd_putscript_cancel(ctx, nonsync);
+	}
+
+	if (!nonsync) {
+		client_send_no(cmd, "MANAGESIEVE does not support the literal synchronous mode.");
+		return cmd_putscript_cancel(ctx, nonsync);
+	}
+
+	if (ctx->script_size == 0) {
+		/* no message data, abort */
+		client_send_no(cmd, "PUTSCRIPT aborted.");
+		cmd_putscript_finish(ctx);
+		return TRUE;
+	}
+
+	/* save the mail */
+	ctx->input = i_stream_create_limit(default_pool, client->input,
+					   client->input->v_offset,
+					   ctx->script_size);
+	ctx->save_ctx = sieve_storage_save_init(ctx->storage, ctx->scriptname, ctx->input);
+
+	if ( ctx->save_ctx == NULL ) {
+		/* save initialization failed */
+		client_send_storage_error(cmd, ctx->storage);
+		return cmd_putscript_cancel(ctx, nonsync);
+	}
+
+	/* after literal comes CRLF, if we fail make sure we eat it away */
+	client->input_skip_line = TRUE;
+
+	client->command_pending = TRUE;
+	cmd->func = cmd_putscript_continue_message;
+	return cmd_putscript_continue_message(cmd);
+}
+
+static bool cmd_putscript_continue_message(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+	struct cmd_putscript_context *ctx = cmd->context;
+	size_t size;
+	bool failed;
+
+	if (ctx->save_ctx != NULL) {
+		if (sieve_storage_save_continue(ctx->save_ctx) < 0) {
+			/* we still have to finish reading the script
+			   from client */
+			sieve_storage_save_abort(ctx->save_ctx);
+			ctx->save_ctx = NULL;
+		}
+	}
+
+	if (ctx->save_ctx == NULL) {
+		(void)i_stream_read(ctx->input);
+		(void)i_stream_get_data(ctx->input, &size);
+		i_stream_skip(ctx->input, size);
+	}
+
+	if (ctx->input->eof || client->input->closed) {
+		/* finished */
+		bool all_written = ctx->input->v_offset == ctx->script_size;
+
+		i_stream_unref(&ctx->input);
+		ctx->input = NULL;
+
+		if (ctx->save_ctx == NULL) {
+			/* failed above */
+			client_send_storage_error(cmd, ctx->storage);
+			failed = TRUE;
+		} else if (!all_written) {
+			/* client disconnected before it finished sending the
+			   whole message. */
+			failed = TRUE;
+			sieve_storage_save_abort(ctx->save_ctx);
+		} else if (sieve_storage_save_finish(ctx->save_ctx) < 0) {
+			failed = TRUE;
+			client_send_storage_error(cmd, ctx->storage);
+		} else {
+			failed = client->input->closed;
+		}
+
+		if (failed) {
+			cmd_putscript_finish(ctx);
+			return TRUE;
+		}
+
+		/* prepare for next message */
+		client->command_pending = FALSE;
+		managesieve_parser_reset(ctx->save_parser);
+		cmd->func = cmd_putscript_finish_parsing;
+		return cmd_putscript_finish_parsing(cmd);
+	}
+
+	return FALSE;
+}
+
+bool cmd_putscript(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+	struct cmd_putscript_context *ctx;
+	const char *scriptname;
+
+	/* <scriptname> */
+	if (!client_read_string_args(cmd, 1, &scriptname) || 
+		*scriptname == '\0')
+		return FALSE;
+
+	ctx = p_new(cmd->pool, struct cmd_putscript_context, 1);
+	ctx->cmd = cmd;
+	ctx->client = client;
+	ctx->storage = client->storage;
+	ctx->scriptname = scriptname;
+
+	io_remove(&client->io);
+	client->io = io_add(i_stream_get_fd(client->input), IO_READ,
+			    client_input, client);
+	/* putscript is special because we're only waiting on client input, not
+	   client output, so disable the standard output handler until we're
+	   finished */
+	o_stream_set_flush_callback(client->output, NULL, NULL);
+
+	ctx->save_parser = managesieve_parser_create(client->input, client->output,
+					      managesieve_max_line_length);
+
+	cmd->func = cmd_putscript_continue_parsing;
+	cmd->context = ctx;
+	return cmd_putscript_continue_parsing(cmd);
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve/cmd-setactive.c new/src/managesieve/cmd-setactive.c
--- old/src/managesieve/cmd-setactive.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/cmd-setactive.c	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,25 @@
+#include "common.h"
+#include "commands.h"
+
+#include "sieve-script.h"
+
+bool cmd_setactive(struct client_command_context *cmd)
+{
+	struct sieve_storage *storage = cmd->client->storage;
+	const char *scriptname;
+	bool was_active = FALSE;
+
+	/* <old name> <new name> */
+	if (!client_read_string_args(cmd, 1, &scriptname))
+		return FALSE;
+
+	if (!sieve_storage_script_activate
+			(storage, scriptname, &was_active))
+		client_send_storage_error(cmd, storage);
+	else
+		client_send_ok(cmd, 
+			was_active ? "Script is already active." :
+				"Setactive completed.");
+
+	return TRUE;
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve/commands.c new/src/managesieve/commands.c
--- old/src/managesieve/commands.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/commands.c	2006-07-13 12:02:41.229073588 +0200
@@ -0,0 +1,117 @@
+#include "common.h"
+#include "buffer.h"
+#include "commands.h"
+
+#include <stdlib.h>
+
+/* Might want to combine this somewhere in a commands-common.c 
+ * to avoid duplicate code 
+ */
+
+const struct command managesieve_commands[] = {
+	{ "CAPABILITY", cmd_capability },
+	{ "LOGOUT", cmd_logout },
+	{ "PUTSCRIPT", cmd_putscript },
+	{ "GETSCRIPT", cmd_getscript },
+	{ "SETACTIVE", cmd_setactive },
+	{ "DELETESCRIPT", cmd_deletescript },
+	{ "LISTSCRIPTS", cmd_listscripts },
+	{ "HAVESPACE", cmd_havespace }
+};
+
+#define MANAGESIEVE_COMMANDS_COUNT \
+	(sizeof(managesieve_commands) / sizeof(managesieve_commands[0]))
+
+static buffer_t *cmdbuf;
+static bool cmdbuf_unsorted;
+
+void command_register(const char *name, command_func_t *func)
+{
+	struct command cmd;
+
+	cmd.name = name;
+	cmd.func = func;
+	buffer_append(cmdbuf, &cmd, sizeof(cmd));
+
+	cmdbuf_unsorted = TRUE;
+}
+
+void command_unregister(const char *name)
+{
+	const struct command *cmd;
+	size_t i, size, count;
+
+	cmd = buffer_get_data(cmdbuf, &size);
+	count = size / sizeof(*cmd);
+
+	for (i = 0; i < count; i++) {
+		if (strcasecmp(cmd[i].name, name) == 0) {
+			buffer_delete(cmdbuf, i * sizeof(*cmd), sizeof(*cmd));
+			return;
+		}
+	}
+
+	i_error("Trying to unregister unknown command '%s'", name);
+}
+
+void command_register_array(const struct command *commands, size_t count)
+{
+	cmdbuf_unsorted = TRUE;
+	buffer_append(cmdbuf, commands, sizeof(*commands) * count);
+}
+
+void command_unregister_array(const struct command *commands, size_t count)
+{
+	while (count > 0) {
+		command_unregister(commands->name);
+		count--; commands++;
+	}
+}
+
+static int command_cmp(const void *p1, const void *p2)
+{
+	const struct command *c1 = p1, *c2 = p2;
+
+	return strcasecmp(c1->name, c2->name);
+}
+
+static int command_bsearch(const void *name, const void *cmd_p)
+{
+	const struct command *cmd = cmd_p;
+
+	return strcasecmp(name, cmd->name);
+}
+
+command_func_t *command_find(const char *name)
+{
+	const struct command *cmd;
+	void *base;
+	size_t size;
+
+	base = buffer_get_modifyable_data(cmdbuf, &size);
+	size /= sizeof(struct command);
+
+	if (cmdbuf_unsorted) {
+		qsort(base, size, sizeof(struct command), command_cmp);
+			cmdbuf_unsorted = FALSE;
+	}
+
+	cmd = bsearch(name, base, size, sizeof(struct command),
+		      command_bsearch);
+	return cmd == NULL ? NULL : cmd->func;
+}
+
+void commands_init(void)
+{
+	cmdbuf = buffer_create_dynamic(system_pool,
+				       sizeof(struct command) * 64);
+	cmdbuf_unsorted = FALSE;
+
+	command_register_array(managesieve_commands, MANAGESIEVE_COMMANDS_COUNT);
+}
+
+void commands_deinit(void)
+{
+	command_unregister_array(managesieve_commands, MANAGESIEVE_COMMANDS_COUNT);
+	buffer_free(cmdbuf);
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve/commands.h new/src/managesieve/commands.h
--- old/src/managesieve/commands.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/commands.h	2006-07-13 02:23:08.000000000 +0200
@@ -0,0 +1,46 @@
+#ifndef __COMMANDS_H
+#define __COMMANDS_H
+
+struct client_command_context;
+
+#include "managesieve-parser.h"
+#include "commands-util.h"
+
+typedef bool command_func_t(struct client_command_context *cmd);
+
+struct command {
+	const char *name;
+	command_func_t *func;
+};
+
+/* Register command. Given name parameter must be permanently stored until
+   command is unregistered. */
+void command_register(const char *name, command_func_t *func);
+void command_unregister(const char *name);
+
+/* Register array of commands. */
+void command_register_array(const struct command *commands, size_t count);
+void command_unregister_array(const struct command *commands, size_t count);
+
+command_func_t *command_find(const char *name);
+
+void commands_init(void);
+void commands_deinit(void);
+
+/* MANAGESIEVE commands: */
+
+/* Non-Authenticated State */
+bool cmd_logout(struct client_command_context *cmd);
+
+bool cmd_capability(struct client_command_context *cmd);
+
+/* Authenticated State */
+bool cmd_putscript(struct client_command_context *cmd);
+bool cmd_getscript(struct client_command_context *cmd);
+bool cmd_setactive(struct client_command_context *cmd);
+bool cmd_deletescript(struct client_command_context *cmd);
+bool cmd_listscripts(struct client_command_context *cmd);
+bool cmd_havespace(struct client_command_context *cmd);
+
+
+#endif
diff -r -u -N --exclude=Makefile.in old/src/managesieve/commands-util.c new/src/managesieve/commands-util.c
--- old/src/managesieve/commands-util.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/commands-util.c	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,20 @@
+#include "common.h"
+#include "array.h"
+#include "buffer.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "client.h"
+#include "commands-util.h"
+#include "managesieve-parser.h"
+
+void client_send_storage_error(struct client_command_context *cmd,
+			       struct sieve_storage *storage)
+{
+	const char *error;
+
+	error = sieve_storage_get_last_error(storage);
+
+	client_send_no(cmd, error);
+}
+
+
diff -r -u -N --exclude=Makefile.in old/src/managesieve/commands-util.h new/src/managesieve/commands-util.h
--- old/src/managesieve/commands-util.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/commands-util.h	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,33 @@
+#ifndef __COMMANDS_UTIL_H
+#define __COMMANDS_UTIL_H
+
+#include "sieve-storage.h"
+
+struct msgset_generator_context {
+	string_t *str;
+	uint32_t first_uid, last_uid;
+};
+
+struct mail_full_flags;
+struct mailbox_keywords;
+
+/* Finds mail storage for given mailbox from namespaces. If not found,
+   sends "Unknown namespace" error message to client. */
+//struct mail_storage *
+//client_find_storage(struct client_command_context *cmd, const char **mailbox);
+
+/* If should_exist is TRUE, this function returns TRUE if the mailbox
+   exists. If it doesn't exist but would be a valid mailbox name, the
+   error message is prefixed with [TRYCREATE].
+
+   If should_exist is FALSE, the should_not_exist specifies if we should
+   return TRUE or FALSE if mailbox doesn't exist. */
+bool client_verify_mailbox_name(struct client_command_context *cmd,
+				const char *scriptname,
+				bool should_exist, bool should_not_exist);
+
+/* Send last mail storage error message to client. */
+void client_send_storage_error(struct client_command_context *cmd,
+			       struct sieve_storage *storage);
+
+#endif
diff -r -u -N --exclude=Makefile.in old/src/managesieve/common.h new/src/managesieve/common.h
--- old/src/managesieve/common.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/common.h	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,35 @@
+#ifndef __COMMON_H
+#define __COMMON_H
+
+#include "lib.h"
+#include "client.h"
+
+/* Disconnect client after idling this many seconds */
+#define CLIENT_IDLE_TIMEOUT (60*30)
+
+/* If we can't send anything to client for this long, disconnect the client */
+#define CLIENT_OUTPUT_TIMEOUT (5*60)
+
+/* Stop buffering more data into output stream after this many bytes */
+#define CLIENT_OUTPUT_OPTIMAL_SIZE 2048
+
+/* Disconnect client when it sends too many bad commands in a row */
+#define CLIENT_MAX_BAD_COMMANDS 20
+
+/* RFC-2683 recommends at least 8000 bytes. Some clients however don't
+   break large message sets to multiple commands, so we're pretty liberal
+   by default. */
+#define DEFAULT_MANAGESIEVE_MAX_LINE_LENGTH 65536
+
+enum client_workarounds {
+  WORKAROUND_DELAY_NONE    = 0x00,
+};
+
+extern struct ioloop *ioloop;
+extern unsigned int managesieve_max_line_length;
+extern enum client_workarounds client_workarounds;
+
+//extern void (*hook_mail_storage_created)(struct sieve_storage *storage);
+extern void (*hook_client_created)(struct client **client);
+
+#endif
diff -r -u -N --exclude=Makefile.in old/src/managesieve/main.c new/src/managesieve/main.c
--- old/src/managesieve/main.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/main.c	2006-07-13 12:01:27.684061174 +0200
@@ -0,0 +1,250 @@
+#include "common.h"
+#include "ioloop.h"
+#include "network.h"
+#include "ostream.h"
+#include "str.h"
+#include "lib-signals.h"
+#include "restrict-access.h"
+#include "fd-close-on-exec.h"
+#include "process-title.h"
+#include "randgen.h"
+#include "module-dir.h"
+#include "dict-client.h"
+#include "commands.h"
+#include "sieve-storage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#define IS_STANDALONE() \
+        (getenv("LOGGED_IN") == NULL)
+
+struct client_workaround_list {
+	const char *name;
+	enum client_workarounds num;
+};
+
+struct client_workaround_list client_workaround_list[] = {
+	{ NULL, 0 }
+};
+
+struct ioloop *ioloop;
+unsigned int managesieve_max_line_length;
+enum client_workarounds client_workarounds = 0;
+static struct io *log_io = NULL;
+
+static struct module *modules;
+static char log_prefix[128]; /* syslog() needs this to be permanent */
+
+//void (*hook_mail_storage_created)(struct mail_storage *storage) = NULL;
+void (*hook_client_created)(struct client **client) = NULL;
+
+static void sig_die(int signo, void *context __attr_unused__)
+{
+	/* warn about being killed because of some signal, except SIGINT (^C)
+	   which is too common at least while testing :) */
+	if (signo != SIGINT)
+		i_warning("Killed with signal %d", signo);
+	io_loop_stop(ioloop);
+}
+
+static void log_error_callback(void *context __attr_unused__)
+{
+	io_loop_stop(ioloop);
+}
+
+static void parse_workarounds(void)
+{
+	struct client_workaround_list *list;
+	const char *env, *const *str;
+
+	env = getenv("MANAGESIEVE_CLIENT_WORKAROUNDS");
+	if (env == NULL)
+		return;
+
+	for (str = t_strsplit_spaces(env, " ,"); *str != NULL; str++) {
+		list = client_workaround_list;
+		for (; list->name != NULL; list++) {
+			if (strcasecmp(*str, list->name) == 0) {
+				client_workarounds |= list->num;
+				break;
+			}
+		}
+		if (list->name == NULL)
+			i_fatal("Unknown client workaround: %s", *str);
+	}
+}
+
+static void open_logfile(void)
+{
+	const char *user;
+
+	if (getenv("LOG_TO_MASTER") != NULL) {
+		i_set_failure_internal();
+		return;
+	}
+
+	user = getenv("USER");
+	if (user == NULL) {
+		if (IS_STANDALONE())
+			user = getlogin();
+		if (user == NULL)
+			user = "??";
+	}
+	if (strlen(user) >= sizeof(log_prefix)-6) {
+		/* quite a long user name, cut it */
+		user = t_strndup(user, sizeof(log_prefix)-6-2);
+		user = t_strconcat(user, "..", NULL);
+	}
+	i_snprintf(log_prefix, sizeof(log_prefix), "managesieve(%s)", user);
+
+	if (getenv("USE_SYSLOG") != NULL) {
+		const char *env = getenv("SYSLOG_FACILITY");
+		i_set_failure_syslog(log_prefix, LOG_NDELAY,
+				     env == NULL ? LOG_MAIL : atoi(env));
+	} else {
+		/* log to file or stderr */
+		i_set_failure_file(getenv("LOGFILE"), log_prefix);
+	}
+
+	if (getenv("INFOLOGFILE") != NULL)
+		i_set_info_file(getenv("INFOLOGFILE"));
+
+	i_set_failure_timestamp_format(getenv("LOGSTAMP"));
+}
+
+static void drop_privileges(void)
+{
+	/* Log file or syslog opening probably requires roots */
+	open_logfile();
+
+	/* Most likely needed. Have to open /dev/urandom before possible
+	   chrooting. */
+	random_init();
+
+	restrict_access_by_env(!IS_STANDALONE());
+}
+
+static void main_init(void)
+{
+	struct sieve_storage *storage;
+	struct client *client;
+	const char *user, *str, *mail;
+
+	lib_signals_init();
+        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
+        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
+        lib_signals_ignore(SIGPIPE);
+        lib_signals_set_handler(SIGALRM, FALSE, NULL, NULL);
+
+	user = getenv("USER");
+	if (user == NULL) {
+		if (IS_STANDALONE())
+			user = getlogin();
+		if (user == NULL)
+			i_fatal("USER environment missing");
+	}
+
+	if (getenv("DEBUG") != NULL) {
+		i_info("Effective uid=%s, gid=%s",
+		       dec2str(geteuid()), dec2str(getegid()));
+	}
+
+	if (getenv("STDERR_CLOSE_SHUTDOWN") != NULL) {
+		/* If master dies, the log fd gets closed and we'll quit */
+		log_io = io_add(STDERR_FILENO, IO_ERROR,
+				log_error_callback, NULL);
+	}
+
+	dict_client_register();
+	clients_init();
+	commands_init();
+
+	if (getenv("MAIL_PLUGINS") == NULL)
+		modules = NULL;
+	else {
+		const char *plugin_dir = getenv("MAIL_PLUGIN_DIR");
+
+		if (plugin_dir == NULL)
+			plugin_dir = MODULEDIR"/managesieve";
+		modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"),
+					  TRUE);
+	}
+
+	str = getenv("MANAGESIEVE_MAX_LINE_LENGTH");
+	managesieve_max_line_length = str != NULL ?
+		(unsigned int)strtoul(str, NULL, 10) :
+		DEFAULT_MANAGESIEVE_MAX_LINE_LENGTH;
+
+	mail = getenv("MAIL");
+
+	storage = sieve_storage_create(mail, user);
+	if (storage == NULL) {
+		/* failed */
+		if (mail != NULL && *mail != '\0')
+			i_fatal("Failed to create sieve storage with mail-data: %s", mail);
+		else {
+			const char *home;
+	    
+			home = getenv("HOME");
+			if (home == NULL) home = "not set";
+	    
+			i_fatal("MAIL environment missing and "
+				"autodetection failed (home %s)", home);
+		}
+	}
+	
+	parse_workarounds();
+
+	client = client_create(0, 1, storage);
+}
+
+static void main_deinit(void)
+{
+	if (log_io != NULL)
+		io_remove(&log_io);
+	clients_deinit();
+
+	module_dir_unload(&modules);
+	commands_deinit();
+	//mail_storage_deinit();
+	dict_client_unregister();
+	random_deinit();
+
+	lib_signals_deinit();
+	closelog();
+}
+
+int main(int argc __attr_unused__, char *argv[], char *envp[])
+{
+#ifdef DEBUG
+	if (getenv("LOGGED_IN") != NULL && getenv("GDB") == NULL)
+		fd_debug_verify_leaks(3, 1024);
+#endif
+	if (IS_STANDALONE() && getuid() == 0 &&
+	    net_getpeername(1, NULL, NULL) == 0) {
+		printf("NO \"managesieve binary must not be started from "
+		       "inetd, use managesieve-login instead.\"\n");
+		return 1;
+	}
+
+	/* NOTE: we start rooted, so keep the code minimal until
+	   restrict_access_by_env() is called */
+	lib_init();
+	drop_privileges();
+
+	process_title_init(argv, envp);
+	ioloop = io_loop_create(system_pool);
+
+	main_init();
+
+	io_loop_run(ioloop);
+	main_deinit();
+
+	io_loop_destroy(&ioloop);
+	lib_deinit();
+
+	return 0;
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve/Makefile.am new/src/managesieve/Makefile.am
--- old/src/managesieve/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/Makefile.am	2006-07-13 02:22:33.000000000 +0200
@@ -0,0 +1,56 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = managesieve
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-dict \
+	-I$(top_srcdir)/src/lib-managesieve \
+	-DMODULEDIR=\""$(moduledir)"\" \
+	-I$(top_srcdir)/src/lib-sievestorage 
+
+managesieve_LDFLAGS = -export-dynamic
+
+# get some functions included which only plugins use. liblib should probably
+# be a shared library so this wouldn't be needed..
+unused_objects = \
+	../lib/mountpoint.o
+
+libs = \
+	../lib-managesieve/libmanagesieve.a \
+  ../lib-sievestorage/libsievestorage.a \
+	../lib-dict/libdict.a \
+	../lib-charset/libcharset.a \
+	../lib/liblib.a \
+	$(unused_objects) 
+
+managesieve_LDADD = \
+	$(libs) \
+	$(LIBICONV) \
+	$(RAND_LIBS) \
+	$(MODULE_LIBS)
+
+managesieve_DEPENDENCIES = $(libs)
+
+cmds = \
+	cmd-capability.c \
+	cmd-logout.c \
+	cmd-putscript.c \
+	cmd-getscript.c \
+	cmd-setactive.c \
+	cmd-deletescript.c \
+	cmd-listscripts.c \
+	cmd-havespace.c 
+
+managesieve_SOURCES = \
+	$(cmds) \
+	client.c \
+	commands.c \
+	commands-util.c \
+	main.c 
+
+noinst_HEADERS = \
+	client.h \
+	commands.h \
+	commands-util.h \
+	common.h 
diff -r -u -N --exclude=Makefile.in old/src/managesieve/README new/src/managesieve/README
--- old/src/managesieve/README	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve/README	2006-07-13 01:36:40.000000000 +0200
@@ -0,0 +1,50 @@
+Design
+------------
+
+The overall design of the daemon is entirely borrowed from the existing
+imap daemon. I have tried to apply the dovecot framework and programming
+paradigms as often as possible. This patch adds the following directories
+to the source directory:
+
+lib-managesieve: parser and quote functions
+lib-sievestorage: defines a storage for the sieve scripts (a bit crude)
+managesieve-login: the initial login daemon (derived from src/imap-login)
+managesieve: the actual managesieve daemon (derived form src/imap)
+
+Currently the managesieve daemon simply plugs in the dovecot-master as any
+other mail protocol (imap,pop3). Since managesieve is not actually a mail
+protocol, this is probably not a good design practice. However, i designed
+this patch to be most uninvasive to the existing sources. I think Timo 
+knows best how to implement this link properly.
+
+The managesieve daemon is currently not very configurable. It uses the 
+default_mail_env to determine the directory for the sieve storage. It 
+maintains a symlink called .dovecot.sieve in that directory. This 
+link points to a script in the sieve subdirectory. Scripts are uploaded 
+in that sub-directory. The sieve directory structure is automatically 
+created if it does not exist. 
+
+It currently implements all existing MANAGESIEVE commands except the 
+HAVESPACE command which always says 'ok'. It also implements the required
+support for UTF-8 strings. 
+
+TODO
+------------
+
+* Enforce protocol syntax better with some of the commands. Some 
+  commands still allow spurious extra arguments. 
+* Create proper process interface for generic non-mail protocols.
+* Make the daemon much more configurable
+* Uploaded scripts are not syntaxchecked yet. I did not take the time to
+  link it to the CMU sieve library. 
+* Make the sieve storage a base class with (possibly) various 
+  implementations, just like mail-storage. Currently not very useful. 
+* Implement the HAVESPACE command properly. Currently it always says ok.
+  Maybe this should be linked to the mail quota system that is currently 
+  developed. 
+* Thorough testing...
+
+Contact info
+------------
+
+Stephan Bosch <stephan at rename-it dot nl>
diff -r -u -N --exclude=Makefile.in old/src/managesieve-login/client-authenticate.c new/src/managesieve-login/client-authenticate.c
--- old/src/managesieve-login/client-authenticate.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve-login/client-authenticate.c	2006-07-13 12:08:19.545732642 +0200
@@ -0,0 +1,271 @@
+#include "common.h"
+#include "base64.h"
+#include "buffer.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "safe-memset.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "managesieve-parser.h"
+#include "managesieve-quote.h"
+#include "auth-client.h"
+#include "client.h"
+#include "client-authenticate.h"
+
+#include <stdlib.h>
+
+const char *client_authenticate_get_capabilities(bool secured)
+{
+	const struct auth_mech_desc *mech;
+	unsigned int i, count;
+	string_t *str;
+
+	str = t_str_new(128);
+	mech = auth_client_get_available_mechs(auth_client, &count);
+
+	if ( count > 0 ) {
+		if ((mech[0].flags & MECH_SEC_PRIVATE) == 0 &&
+				(secured || !disable_plaintext_auth ||
+				(mech[0].flags & MECH_SEC_PLAINTEXT) == 0)) {
+			str_append(str, mech[0].name);
+		}
+     
+		for (i = 1; i < count; i++) {
+			/* a) transport is secured
+			   b) auth mechanism isn't plaintext
+		  	 c) we allow insecure authentication
+			*/
+			if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
+			    (secured || !disable_plaintext_auth ||
+		  	   (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) {
+				str_append_c(str, ' ');
+				str_append(str, mech[i].name);
+			}
+		}
+	}
+
+	return str_c(str);
+}
+
+static void client_auth_input(void *context)
+{
+	struct managesieve_client *client = context;
+	struct managesieve_arg *args;
+	const char *msg;
+	char *line;
+	bool fatal;
+
+	if (!client_read(client))
+		return;
+
+	if (client->skip_line) {
+		if (i_stream_next_line(client->input) == NULL)
+			return;
+
+		client->skip_line = FALSE;
+	}
+
+	switch (managesieve_parser_read_args(client->parser, 1, 0, &args)) {
+	case -1:
+		/* error */
+		msg = managesieve_parser_get_error(client->parser, &fatal);
+		if (fatal) {
+			/* FIXME: What to do? */
+		}
+	  
+		sasl_server_auth_client_error(&client->common, msg);
+		return;
+	case -2:
+		/* not enough data */
+		return;
+	}
+
+	if (args[0].type != MANAGESIEVE_ARG_STRING) {
+		sasl_server_auth_client_error(&client->common, "Invalid response string.");
+		managesieve_parser_reset(client->parser);
+		return;
+	}
+
+	line = MANAGESIEVE_ARG_STR(&args[0]);
+
+	if (client->common.auth_request == NULL) {
+		sasl_server_auth_client_error(&client->common,
+					      "Don't send unrequested data");
+	} else {
+		auth_client_request_continue(client->common.auth_request, line);
+		client->common.auth_request = NULL;
+	}
+
+	/* clear sensitive data */
+	managesieve_parser_reset(client->parser);
+	safe_memset(line, 0, strlen(line));
+}
+
+static bool client_handle_args(struct managesieve_client *client,
+			       const char *const *args, bool nologin)
+{
+	const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL;
+	string_t *refer;
+	unsigned int port = 143;
+	bool temp = FALSE;
+
+	for (; *args != NULL; args++) {
+		if (strcmp(*args, "nologin") == 0)
+			nologin = TRUE;
+		else if (strcmp(*args, "temp") == 0)
+			temp = TRUE;
+		else if (strncmp(*args, "reason=", 7) == 0)
+			reason = *args + 7;
+		else if (strncmp(*args, "host=", 5) == 0)
+			host = *args + 5;
+		else if (strncmp(*args, "port=", 5) == 0)
+			port = atoi(*args + 5);
+		else if (strncmp(*args, "pass=", 5) == 0)
+			pass = *args + 5;
+	}
+
+	if (destuser == NULL)
+		destuser = client->common.virtual_user;
+
+	if (host != NULL) {
+		/* MANAGESIEVE referral
+
+		   [nologin] referral host=.. [port=..] [destuser=..]
+		   [reason=..]
+
+		   NO (REFERRAL sieve://user;AUTH=mech@host:port/) Can't login.
+		   OK (...) Logged in, but you should use this server instead.
+		   .. [REFERRAL ..] (Reason from auth server)
+		*/
+		refer = t_str_new(128);
+		str_printfa(refer, "REFERRAL sieve://%s;AUTH=%s@%s",
+			    destuser, client->common.auth_mech_name, host);
+		if (port != 2000)
+			str_printfa(refer, ":%u", port);
+
+		if (reason == NULL) {
+			if (nologin)
+				reason = "Try this server instead.";
+			else 
+				reason = "Logged in, but you should use "
+					"this server instead.";
+		}
+
+		if (!nologin) {
+			client_send_okresp(client, str_c(refer), reason);
+			client_destroy(client, "Login with referral");
+			return TRUE;
+		}
+		client_send_noresp(client, str_c(refer), reason);
+	} else if (nologin) {
+		/* Authentication went ok, but for some reason user isn't
+		   allowed to log in. Shouldn't probably happen. */
+		if (reason != NULL)
+			client_send_no(client, reason);
+		else if (temp)
+			client_send_no(client, AUTH_TEMP_FAILED_MSG);		
+		else
+			client_send_no(client, AUTH_FAILED_MSG);
+	} else {
+		/* normal login/failure */
+		return FALSE;
+	}
+
+	i_assert(nologin);
+
+	/* get back to normal client input. */
+	if (client->io != NULL)
+		io_remove(&client->io);
+	client->io = io_add(client->common.fd, IO_READ, client_input, client);
+	return TRUE;
+}
+
+static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
+			  const char *data, const char *const *args)
+{
+	struct managesieve_client *client = (struct managesieve_client *)_client;
+	string_t *str;
+
+	client->skip_line = TRUE;
+
+	switch (reply) {
+	case SASL_SERVER_REPLY_SUCCESS:
+		if (args != NULL) {
+			if (client_handle_args(client, args, FALSE))
+				break;
+		}
+
+		client_send_ok(client, "Logged in.");
+		client_destroy(client, "Login");
+		break;
+	case SASL_SERVER_REPLY_AUTH_FAILED:
+	case SASL_SERVER_REPLY_CLIENT_ERROR:
+		if (args != NULL) {
+			if (client_handle_args(client, args, TRUE))
+				break;
+		}
+
+		client_send_no(client, data != NULL ? data : AUTH_FAILED_MSG);
+
+		/* get back to normal client input. */
+		if (client->io != NULL)
+			io_remove(&client->io);
+		client->io = io_add(client->common.fd, IO_READ,
+				    client_input, client);
+		break;
+	case SASL_SERVER_REPLY_MASTER_FAILED:
+		client_destroy_internal_failure(client);
+		break;
+	case SASL_SERVER_REPLY_CONTINUE:
+		t_push();
+		str = t_str_new(256);
+		managesieve_quote_append_string(str, data, TRUE);
+
+				
+		/* don't check return value here. it gets tricky if we try
+		   to call client_destroy() in here. */
+		(void)o_stream_send(client->output, str_c(str), str_len(str));
+		t_pop();
+
+		return;
+	}
+
+	client_unref(client);
+}
+
+int cmd_authenticate(struct managesieve_client *client, struct managesieve_arg *args)
+{
+	const char *mech_name, *init_resp = NULL;
+
+	/* we want only one argument: authentication mechanism name */
+	if (args[0].type != MANAGESIEVE_ARG_STRING)
+		return -1;
+	if (args[1].type != MANAGESIEVE_ARG_EOL) {
+		/* optional SASL initial response */
+		if (args[1].type != MANAGESIEVE_ARG_STRING ||
+		    args[2].type != MANAGESIEVE_ARG_EOL)
+			return -1;
+		init_resp = MANAGESIEVE_ARG_STR(&args[1]);
+	}
+
+	mech_name = MANAGESIEVE_ARG_STR(&args[0]);
+	if (*mech_name == '\0')
+		return 0;
+
+	client_ref(client);
+	sasl_server_auth_begin(&client->common, "MANAGESIEVE", mech_name,
+			       init_resp, sasl_callback);
+	if (!client->common.authenticating)
+		return 1;
+
+	/* following input data will go to authentication */
+	if (client->io != NULL)
+		io_remove(&client->io);
+	managesieve_parser_reset(client->parser);
+	client->io = io_add(client->common.fd, IO_READ,
+			    client_auth_input, client);
+
+	return 0;
+}
+
diff -r -u -N --exclude=Makefile.in old/src/managesieve-login/client-authenticate.h new/src/managesieve-login/client-authenticate.h
--- old/src/managesieve-login/client-authenticate.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve-login/client-authenticate.h	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,9 @@
+#ifndef __CLIENT_AUTHENTICATE_H
+#define __CLIENT_AUTHENTICATE_H
+
+const char *client_authenticate_get_capabilities(bool secured);
+
+int cmd_login(struct managesieve_client *client, struct managesieve_arg *args);
+int cmd_authenticate(struct managesieve_client *client, struct managesieve_arg *args);
+
+#endif
diff -r -u -N --exclude=Makefile.in old/src/managesieve-login/client.c new/src/managesieve-login/client.c
--- old/src/managesieve-login/client.c	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve-login/client.c	2006-07-13 12:06:30.944236747 +0200
@@ -0,0 +1,626 @@
+#include "common.h"
+#include "buffer.h"
+#include "hash.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "process-title.h"
+#include "safe-memset.h"
+#include "str.h"
+#include "strescape.h"
+#include "managesieve-parser.h"
+#include "managesieve-quote.h"
+
+#include "client.h"
+#include "client-authenticate.h"
+#include "auth-client.h"
+#include "ssl-proxy.h"
+
+/* max. size of one parameter in line, or max reply length in SASL
+   authentication */
+#define MAX_INBUF_SIZE 4096
+
+/* max. size of output buffer. if it gets full, the client is disconnected.
+   SASL authentication gives the largest output. */
+#define MAX_OUTBUF_SIZE 4096
+
+/* maximum length for MANAGESIEVE command line. */
+#define MAX_MANAGESIEVE_LINE 8192
+
+/* Disconnect client after idling this many seconds */
+#define CLIENT_LOGIN_IDLE_TIMEOUT 60
+
+/* Disconnect client when it sends too many bad commands */
+#define CLIENT_MAX_BAD_COMMANDS 10
+
+/* When max. number of simultaneous connections is reached, few of the
+   oldest connections are disconnected. Since we have to go through the whole
+   client hash, it's faster if we disconnect multiple clients. */
+#define CLIENT_DESTROY_OLDEST_COUNT 16
+
+#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
+#  error client idle timeout must be smaller than authentication timeout
+#endif
+
+const char *login_protocol = "MANAGESIEVE";
+const char *capability_string = CAPABILITY_STRING;
+
+static struct hash_table *clients;
+static struct timeout *to_idle;
+
+static void client_set_title(struct managesieve_client *client)
+{
+	const char *addr;
+
+	if (!verbose_proctitle || !process_per_connection)
+		return;
+
+	addr = net_ip2addr(&client->common.ip);
+	if (addr == NULL)
+		addr = "??";
+
+	process_title_set(t_strdup_printf(client->common.tls ?
+					  "[%s TLS]" : "[%s]", addr));
+}
+
+static void client_open_streams(struct managesieve_client *client, int fd)
+{
+	client->input = i_stream_create_file(fd, default_pool,
+					     MAX_INBUF_SIZE, FALSE);
+	client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
+					      FALSE);
+	client->parser = managesieve_parser_create(client->input, client->output,
+					    MAX_MANAGESIEVE_LINE);
+}
+
+/* Skip incoming data until newline is found,
+   returns TRUE if newline was found. */
+bool client_skip_line(struct managesieve_client *client)
+{
+	const unsigned char *data;
+	size_t i, data_size;
+
+	data = i_stream_get_data(client->input, &data_size);
+
+	for (i = 0; i < data_size; i++) {
+		if (data[i] == '\n') {
+			i_stream_skip(client->input, i+1);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void client_send_capabilities(struct managesieve_client *client)
+{
+	const char *auths;
+
+	/* We assume no MANAGESIEVE-string incompatible values are produced here */
+
+	client_send_line(client, "\"IMPLEMENTATION\" \"" PACKAGE "\"");
+
+	auths = client_authenticate_get_capabilities(client->common.secured);
+  
+	client_send_line(client, t_strconcat("\"SASL\" \"", auths, "\"", NULL) );
+
+	client_send_line(client, "\"SIEVE\" \"FILEINTO VACATION\"");
+
+	if (ssl_initialized && !client->common.tls)
+		client_send_line(client, "\"STARTTLS\"" );
+}
+
+static int cmd_capability(struct managesieve_client *client)
+{
+	client_send_capabilities(client);
+	client_send_ok(client, "Capability completed.");
+	return TRUE;
+}
+
+static void client_start_tls(struct managesieve_client *client)
+{
+	int fd_ssl;
+
+	fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
+			       &client->common.proxy);
+	if (fd_ssl == -1) {
+		client_send_bye(client, "TLS initialization failed.");
+		client_destroy(client, "TLS initialization failed.");
+		return;
+	}
+
+	client->common.tls = TRUE;
+	client->common.secured = TRUE;
+	client_set_title(client);
+
+	client->common.fd = fd_ssl;
+	i_stream_unref(&client->input);
+	o_stream_unref(&client->output);
+	managesieve_parser_destroy(&client->parser);
+
+	/* CRLF is lost from buffer when streams are reopened. */
+	client->skip_line = FALSE;
+
+	client_open_streams(client, fd_ssl);
+	client->io = io_add(client->common.fd, IO_READ, client_input, client);
+}
+
+static int client_output_starttls(void *context)
+{
+	struct managesieve_client *client = context;
+	int ret;
+
+	if ((ret = o_stream_flush(client->output)) < 0) {
+		client_destroy(client, "Disconnected");
+		return 1;
+	}
+
+	if (ret > 0) {
+		o_stream_set_flush_callback(client->output, NULL, NULL);
+		client_start_tls(client);
+	}
+	return 1;
+}
+
+static int cmd_starttls(struct managesieve_client *client)
+{
+	if (client->common.tls) {
+		client_send_no(client, "TLS is already active.");
+		return 1;
+	}
+
+	if (!ssl_initialized) {
+		client_send_no(client, "TLS support isn't enabled.");
+		return 1;
+	}
+
+	/* remove input handler, SSL proxy gives us a new fd. we also have to
+	   remove it in case we have to wait for buffer to be flushed */
+	if (client->io != NULL)
+		io_remove(&client->io);
+
+	client_send_ok(client, "Begin TLS negotiation now.");
+
+	/* uncork the old fd */
+	o_stream_uncork(client->output);
+
+	if (o_stream_flush(client->output) <= 0) {
+		/* the buffer has to be flushed */
+		o_stream_set_flush_pending(client->output, TRUE);
+		o_stream_set_flush_callback(client->output,
+					    client_output_starttls, client);
+	} else {
+		client_start_tls(client);
+	}
+
+	client_send_capabilities(client);
+	client_send_ok(client, "TLS negotiation successful.");
+	return 1;
+}
+
+static int cmd_logout(struct managesieve_client *client)
+{
+	client_send_ok(client, "Logout completed.");
+	client_destroy(client, "Aborted login (logout command)");
+	return 1;
+}
+
+static int client_command_execute(struct managesieve_client *client, const char *cmd,
+				  struct managesieve_arg *args)
+{
+	cmd = t_str_ucase(cmd);
+	if (strcmp(cmd, "AUTHENTICATE") == 0)
+		return cmd_authenticate(client, args);
+	if (strcmp(cmd, "CAPABILITY") == 0)
+		return cmd_capability(client);
+	if (strcmp(cmd, "STARTTLS") == 0)
+		return cmd_starttls(client);
+	if (strcmp(cmd, "LOGOUT") == 0)
+		return cmd_logout(client);
+
+	return -1;
+}
+
+static bool client_handle_input(struct managesieve_client *client)
+{
+	struct managesieve_arg *args;
+	const char *msg;
+	int ret;
+	bool fatal;
+
+	i_assert(!client->common.authenticating);
+
+	if (client->cmd_finished) {
+		/* clear the previous command from memory. don't do this
+		   immediately after handling command since we need the
+		   cmd_tag to stay some time after authentication commands. */
+		client->cmd_name = NULL;
+		managesieve_parser_reset(client->parser);
+
+		/* remove \r\n */
+		if (client->skip_line) {
+			if (!client_skip_line(client))
+				return FALSE;
+			client->skip_line = FALSE;
+		}
+
+		client->cmd_finished = FALSE;
+	}
+
+	if (client->cmd_name == NULL) {
+		client->cmd_name = managesieve_parser_read_word(client->parser);
+		if (client->cmd_name == NULL)
+			return FALSE; /* need more data */
+	}
+
+	switch (managesieve_parser_read_args(client->parser, 0, 0, &args)) {
+	case -1:
+		/* error */
+		msg = managesieve_parser_get_error(client->parser, &fatal);
+		if (fatal) {
+			client_send_bye(client, msg);
+			client_destroy(client, t_strconcat("Disconnected: ",
+							   msg, NULL));
+			return FALSE;
+		}
+
+		client_send_no(client, msg);
+		client->cmd_finished = TRUE;
+		client->skip_line = TRUE;
+		return TRUE;
+	case -2:
+		/* not enough data */
+		return FALSE;
+	}
+	client->skip_line = TRUE;
+
+	ret = client_command_execute(client, client->cmd_name, args);
+
+	client->cmd_finished = TRUE;
+	if (ret < 0) {
+		if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
+			client_send_bye(client,	
+				"Too many invalid MANAGESIEVE commands.");
+			client_destroy(client, "Disconnected: "
+				"Too many invalid commands.");
+			return FALSE;
+		}  
+		client_send_no(client,
+			"Error in MANAGESIEVE command received by server.");
+	}
+
+	return ret != 0;
+}
+
+bool client_read(struct managesieve_client *client)
+{
+	switch (i_stream_read(client->input)) {
+	case -2:
+		/* buffer full */
+		client_send_bye(client, "Input buffer full, aborting");
+		client_destroy(client, "Disconnected: Input buffer full");
+		return FALSE;
+	case -1:
+		/* disconnected */
+		client_destroy(client, "Disconnected");
+		return FALSE;
+	default:
+		/* something was read */
+		return TRUE;
+	}
+}
+
+void client_input(void *context)
+{
+	struct managesieve_client *client = context;
+
+	client->last_input = ioloop_time;
+
+	if (!client_read(client))
+		return;
+
+	client_ref(client);
+
+	if (!auth_client_is_connected(auth_client)) {
+		/* we're not yet connected to auth process -
+		   don't allow any commands */
+		/* FIXME: Can't do this with managesieve. Any other ways?
+		client_send_ok(client,
+		"Waiting for authentication process to respond.");
+		*/
+		client->input_blocked = TRUE;
+	} else {
+		o_stream_cork(client->output);
+		while (client_handle_input(client)) ;
+		o_stream_uncork(client->output);
+	}
+
+	client_unref(client);
+}
+
+static void client_destroy_oldest(void)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+	struct managesieve_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
+	int i;
+
+	/* find the oldest clients and put them to destroy-buffer */
+	memset(destroy_buf, 0, sizeof(destroy_buf));
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct managesieve_client *client = key;
+
+		for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
+			if (destroy_buf[i] == NULL ||
+			    destroy_buf[i]->created > client->created) {
+				/* @UNSAFE */
+				memmove(destroy_buf+i+1, destroy_buf+i,
+					sizeof(destroy_buf) -
+					(i+1) * sizeof(struct managesieve_client *));
+				destroy_buf[i] = client;
+				break;
+			}
+		}
+	}
+	hash_iterate_deinit(iter);
+
+	/* then kill them */
+	for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
+		if (destroy_buf[i] == NULL)
+			break;
+
+		client_destroy(destroy_buf[i],
+			       "Disconnected: Connection queue full");
+	}
+}
+
+static void client_send_greeting(struct managesieve_client *client)
+{
+  /* Send initial capabilities */
+  client_send_capabilities(client);
+  client_send_ok(client, greeting);
+
+  client->greeting_sent = TRUE;
+}
+
+struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
+			     const struct ip_addr *ip)
+{
+	struct managesieve_client *client;
+	const char *addr;
+
+	if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
+	    hash_size(clients) >= max_logging_users) {
+		/* reached max. users count, kill few of the
+		   oldest connections */
+		client_destroy_oldest();
+	}
+
+	/* always use nonblocking I/O */
+	net_set_nonblock(fd, TRUE);
+
+	client = i_new(struct managesieve_client, 1);
+	client->created = ioloop_time;
+	client->refcount = 1;
+	client->common.tls = ssl;
+
+	addr = net_ip2addr(ip);
+	client->common.secured = ssl ||
+		(IPADDR_IS_V4(ip) && strncmp(addr, "127.", 4) == 0) ||
+		(IPADDR_IS_V6(ip) && (strcmp(addr, "::1") == 0 ||
+				      strncmp(addr, "::ffff:127.", 11) == 0));
+
+	client->common.local_ip = *local_ip;
+	client->common.ip = *ip;
+	client->common.fd = fd;
+
+	client_open_streams(client, fd);
+	client->io = io_add(fd, IO_READ, client_input, client);
+
+	client->last_input = ioloop_time;
+	hash_insert(clients, client, client);
+
+	main_ref();
+
+	if (auth_client_is_connected(auth_client))
+		client_send_greeting(client);
+	client_set_title(client);
+
+	client->created = TRUE;
+	return &client->common;
+}
+
+void client_destroy(struct managesieve_client *client, const char *reason)
+{
+	if (client->destroyed)
+		return;
+	client->destroyed = TRUE;
+
+	if (reason != NULL)
+		client_syslog(&client->common, reason);
+
+	hash_remove(clients, client);
+
+	if (client->input != NULL)
+		i_stream_close(client->input);
+	if (client->output != NULL)
+		o_stream_close(client->output);
+
+	if (client->common.auth_request != NULL) {
+		auth_client_request_abort(client->common.auth_request);
+                client->common.auth_request = NULL;
+	}
+
+	if (client->common.master_tag != 0)
+		master_request_abort(&client->common);
+
+	if (client->io != NULL)
+		io_remove(&client->io);
+
+	if (client->common.fd != -1) {
+		net_disconnect(client->common.fd);
+		client->common.fd = -1;
+	}
+
+	if (client->common.proxy != NULL) {
+		ssl_proxy_free(client->common.proxy);
+		client->common.proxy = NULL;
+	}
+
+	client_unref(client);
+}
+
+void client_destroy_internal_failure(struct managesieve_client *client)
+{
+	client_send_bye(client, "Internal login failure. "
+		"Refer to server log for more information.");
+	client_destroy(client, "Internal login failure");
+}
+
+void client_ref(struct managesieve_client *client)
+{
+	client->refcount++;
+}
+
+bool client_unref(struct managesieve_client *client)
+{
+	i_assert(client->refcount > 0);
+	if (--client->refcount > 0)
+		return TRUE;
+
+	i_assert(client->destroyed);
+
+	managesieve_parser_destroy(&client->parser);
+
+	if (client->input != NULL)
+		i_stream_unref(&client->input);
+	if (client->output != NULL)
+		o_stream_unref(&client->output);
+
+	i_free(client->common.virtual_user);
+	i_free(client->common.auth_mech_name);
+	i_free(client);
+
+	main_unref();
+	return FALSE;
+}
+
+void client_send_line(struct managesieve_client *client, const char *line)
+{
+	struct const_iovec iov[2];
+	ssize_t ret;
+
+	iov[0].iov_base = line;
+	iov[0].iov_len = strlen(line);
+	iov[1].iov_base = "\r\n";
+	iov[1].iov_len = 2;
+
+	ret = o_stream_sendv(client->output, iov, 2);
+	if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
+		/* either disconnection or buffer full. in either case we
+		   want this connection destroyed. however destroying it here
+		   might break things if client is still tried to be accessed
+		   without being referenced.. */
+		i_stream_close(client->input);
+	}
+}
+
+void _client_send_response(struct managesieve_client *client, 
+	const char *oknobye, const char *resp_code, const char *msg)
+{
+	string_t *str;
+
+	str = t_str_new(128);
+	str_append(str, oknobye);
+
+	if ( resp_code != NULL )
+	{
+		str_append(str, " (");
+		str_append(str, resp_code);
+		str_append_c(str, ')');
+	}
+
+	if ( msg != NULL )	
+	{
+		str_append_c(str, ' ');
+		managesieve_quote_append_string(str, msg, TRUE);
+	}
+
+  client_send_line(client, str_c(str));
+}
+
+static void client_check_idle(struct managesieve_client *client)
+{
+	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
+		client_send_bye(client, "Disconnected for inactivity.");
+		client_destroy(client, "Disconnected: Inactivity");
+	}
+}
+
+static void idle_timeout(void *context __attr_unused__)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct managesieve_client *client = key;
+
+		client_check_idle(client);
+	}
+	hash_iterate_deinit(iter);
+}
+
+unsigned int clients_get_count(void)
+{
+	return hash_size(clients);
+}
+
+void clients_notify_auth_connected(void)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct managesieve_client *client = key;
+
+		if (!client->greeting_sent)
+			client_send_greeting(client);
+		if (client->input_blocked) {
+			client->input_blocked = FALSE;
+			client_input(client);
+		}
+	}
+	hash_iterate_deinit(iter);
+}
+
+void clients_destroy_all(void)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct managesieve_client *client = key;
+
+		client_destroy(client, NULL);
+	}
+	hash_iterate_deinit(iter);
+}
+
+void clients_init(void)
+{
+	clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
+	to_idle = timeout_add(1000, idle_timeout, NULL);
+}
+
+void clients_deinit(void)
+{
+	clients_destroy_all();
+	hash_destroy(clients);
+
+	timeout_remove(&to_idle);
+}
diff -r -u -N --exclude=Makefile.in old/src/managesieve-login/client.h new/src/managesieve-login/client.h
--- old/src/managesieve-login/client.h	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve-login/client.h	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,59 @@
+#ifndef __CLIENT_H
+#define __CLIENT_H
+
+#include "network.h"
+#include "master.h"
+#include "client-common.h"
+
+struct managesieve_client {
+	struct client common;
+
+	time_t created;
+	int refcount;
+
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+	struct managesieve_parser *parser;
+
+	time_t last_input;
+	unsigned int bad_counter;
+
+	const char *cmd_name;
+
+	unsigned int cmd_finished:1;
+	unsigned int skip_line:1;
+	unsigned int input_blocked:1;
+	unsigned int destroyed:1;
+	unsigned int greeting_sent:1;
+};
+
+void client_destroy(struct managesieve_client *client, const char *reason);
+void client_destroy_internal_failure(struct managesieve_client *client);
+
+void client_send_line(struct managesieve_client *client, const char *line);
+
+bool client_read(struct managesieve_client *client);
+bool client_skip_line(struct managesieve_client *client);
+void client_input(void *context);
+
+void client_ref(struct managesieve_client *client);
+bool client_unref(struct managesieve_client *client);
+
+void _client_send_response(struct managesieve_client *client,
+  const char *oknobye, const char *resp_code, const char *msg);
+
+#define client_send_ok(client, msg) \
+	_client_send_response(client, "OK", NULL, msg)
+#define client_send_no(client, msg) \
+  _client_send_response(client, "NO", NULL, msg)
+#define client_send_bye(client, msg) \
+  _client_send_response(client, "BYE", NULL, msg)
+
+#define client_send_okresp(client, resp_code, msg) \
+  _client_send_response(client, "OK", resp_code, msg)
+#define client_send_noresp(client, resp_code, msg) \
+  _client_send_response(client, "NO", resp_code, msg)
+#define client_send_byeresp(client, resp_code, msg) \
+  _client_send_response(client, "BYE", resp_code, msg)
+#endif
diff -r -u -N --exclude=Makefile.in old/src/managesieve-login/Makefile.am new/src/managesieve-login/Makefile.am
--- old/src/managesieve-login/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ new/src/managesieve-login/Makefile.am	2006-07-13 00:12:02.000000000 +0200
@@ -0,0 +1,23 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = managesieve-login
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-auth \
+	-I$(top_srcdir)/src/lib-managesieve \
+	-I$(top_srcdir)/src/login-common
+
+managesieve_login_LDADD = \
+	../login-common/liblogin-common.a \
+	../lib-managesieve/libmanagesieve.a \
+	../lib-auth/libauth.a \
+	../lib/liblib.a \
+	$(SSL_LIBS)
+
+managesieve_login_SOURCES = \
+	client.c \
+	client-authenticate.c 
+noinst_HEADERS = \
+	client.h \
+	client-authenticate.h 
diff -r -u -N --exclude=Makefile.in old/src/master/common.h new/src/master/common.h
--- old/src/master/common.h	2006-02-05 19:00:15.000000000 +0100
+++ new/src/master/common.h	2006-07-13 12:41:12.665716302 +0200
@@ -17,6 +17,7 @@
 	PROCESS_TYPE_SSL_PARAM,
 	PROCESS_TYPE_DICT,
 
+	PROCESS_TYPE_MANAGESIEVE,
 	PROCESS_TYPE_MAX
 };
 
diff -r -u -N --exclude=Makefile.in old/src/master/login-process.c new/src/master/login-process.c
--- old/src/master/login-process.c	2006-06-16 20:06:35.000000000 +0200
+++ new/src/master/login-process.c	2006-07-13 12:40:50.869425137 +0200
@@ -62,8 +62,20 @@
 
 	group = i_new(struct login_group, 1);
 	group->set = set;
-	group->process_type = set->protocol == MAIL_PROTOCOL_IMAP ?
-		PROCESS_TYPE_IMAP : PROCESS_TYPE_POP3;
+
+	switch ( set->protocol ) {
+		case MAIL_PROTOCOL_IMAP:
+			group->process_type = PROCESS_TYPE_IMAP;
+			break;
+		case MAIL_PROTOCOL_POP3:
+			group->process_type = PROCESS_TYPE_POP3;
+			break;
+		case MAIL_PROTOCOL_MANAGESIEVE:
+			group->process_type = PROCESS_TYPE_MANAGESIEVE;
+			break;
+		default:
+			i_unreached();
+	}
 
 	group->next = login_groups;
 	login_groups = group;
@@ -147,6 +159,8 @@
 			login_group_create(server->imap);
 		if (server->pop3 != NULL)
 			login_group_create(server->pop3);
+		if (server->managesieve != NULL)
+			login_group_create(server->managesieve);
 	}
 }
 
@@ -208,6 +222,8 @@
 			protocol = MAIL_PROTOCOL_IMAP;
 		else if (strcmp(proto, "pop3") == 0)
 			protocol = MAIL_PROTOCOL_POP3;
+		else if (strcmp(proto, "managesieve") == 0)
+			protocol = MAIL_PROTOCOL_MANAGESIEVE;
 		else {
 			i_error("login: Unknown protocol '%s'", proto);
 			return FALSE;
diff -r -u -N --exclude=Makefile.in old/src/master/mail-process.c new/src/master/mail-process.c
--- old/src/master/mail-process.c	2006-07-01 21:40:12.000000000 +0200
+++ new/src/master/mail-process.c	2006-07-13 12:39:32.728381779 +0200
@@ -222,6 +222,9 @@
 	env_put(t_strconcat("POP3_LOGOUT_FORMAT=",
 			    set->pop3_logout_format, NULL));
 
+	env_put(t_strdup_printf("MANAGESIEVE_MAX_LINE_LENGTH=%u",
+				set->managesieve_max_line_length));
+
 	if (set->mail_save_crlf)
 		env_put("MAIL_SAVE_CRLF=1");
 	if (set->mail_read_mmaped)
@@ -330,6 +333,8 @@
 			set = server->imap;
 		else if (strcmp(protocol, "pop3") == 0)
 			set = server->pop3;
+		else if (strcmp(protocol, "managesieve") == 0)
+			set = server->managesieve;
 		else
 			i_fatal("Unknown protocol: '%s'", protocol);
 		executable = set->mail_executable;
diff -r -u -N --exclude=Makefile.in old/src/master/main.c new/src/master/main.c
--- old/src/master/main.c	2006-07-02 15:58:17.000000000 +0200
+++ new/src/master/main.c	2006-07-13 12:35:06.808663593 +0200
@@ -33,7 +33,8 @@
 	"imap",
 	"pop3",
 	"ssl-build-param",
-	"dict"
+	"dict",
+	 "managesieve"
 };
 
 static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf";
@@ -205,6 +206,7 @@
 		switch (process_type) {
 		case PROCESS_TYPE_IMAP:
 		case PROCESS_TYPE_POP3:
+		case PROCESS_TYPE_MANAGESIEVE:
 			mail_process_destroyed(pid);
 			break;
 		case PROCESS_TYPE_SSL_PARAM:
@@ -335,6 +337,10 @@
 			check_conflicts_set(server->pop3, ip, port,
 					    "pop3", proto);
 		}
+		if (server->managesieve != NULL) {
+			check_conflicts_set(server->managesieve, ip, port,
+					    "managesieve", proto);
+		}
 	}
 }
 
@@ -345,13 +351,31 @@
 	unsigned int port;
 	int *fd, i;
 
-	set->listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 143 : 110;
+	switch (set->protocol) {
+	case MAIL_PROTOCOL_IMAP:
+		set->listen_port = 143;
 #ifdef HAVE_SSL
-	set->ssl_listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 993 : 995;
+		set->ssl_listen_port = 993;
 #else
-	set->ssl_listen_port = 0;
+		set->ssl_listen_port = 0;
 #endif
-
+		break;
+	case MAIL_PROTOCOL_POP3:
+		set->listen_port = 110;
+#ifdef HAVE_SSL
+		set->ssl_listen_port = 995;
+#else
+		set->ssl_listen_port = 0;
+#endif
+		break;
+	case MAIL_PROTOCOL_MANAGESIEVE:
+		set->listen_port = 2000;
+		set->ssl_listen_port = 0;
+		break;
+	default:
+		i_unreached();
+	}
+ 
 	/* resolve */
 	resolve_ip(set->listen, &set->listen_ip, &set->listen_port);
 	resolve_ip(set->ssl_listen, &set->ssl_listen_ip, &set->ssl_listen_port);
@@ -391,6 +415,12 @@
 				port = set->ssl_listen_port;
 				ip = &set->ssl_listen_ip;
 			}
+		} else if (strcasecmp(*proto, "managesieve") == 0) {
+			if (set->protocol == MAIL_PROTOCOL_MANAGESIEVE) {
+				fd = &set->listen_fd;
+				port = set->listen_port;
+				ip = &set->listen_ip;
+			}
 		} else {
 			i_fatal("Unknown protocol %s", *proto);
 		}
@@ -434,12 +464,14 @@
 static void listen_fds_open(bool retry)
 {
 	struct server_settings *server;
-
+  
 	for (server = settings_root; server != NULL; server = server->next) {
 		if (server->imap != NULL)
 			listen_protocols(server->imap, retry);
 		if (server->pop3 != NULL)
 			listen_protocols(server->pop3, retry);
+		if (server->managesieve != NULL)
+			listen_protocols(server->managesieve, retry);
 	}
 }
 
@@ -462,6 +494,11 @@
 			    close(server->pop3->ssl_listen_fd) < 0)
 				i_error("close(pop3.ssl_listen_fd) failed: %m");
 		}
+		if (server->managesieve != NULL) {
+			if (server->managesieve->listen_fd != null_fd &&
+			    close(server->managesieve->listen_fd) < 0)
+				i_error("close(managesieve.listen_fd) failed: %m");
+		}
 	}
 }
 
@@ -485,6 +522,8 @@
 			return TRUE;
 		if (server->pop3 != NULL && have_stderr_set(server->pop3))
 			return TRUE;
+		if (server->managesieve != NULL && have_stderr_set(server->managesieve))
+			return TRUE;
 
 		server = server->next;
 	}
diff -r -u -N --exclude=Makefile.in old/src/master/master-settings.c new/src/master/master-settings.c
--- old/src/master/master-settings.c	2006-07-02 21:18:13.000000000 +0200
+++ new/src/master/master-settings.c	2006-07-13 12:38:33.459590926 +0200
@@ -156,6 +156,9 @@
 	DEF(SET_STR, pop3_client_workarounds),
 	DEF(SET_STR, pop3_logout_format),
 
+  /* managesieve */
+  DEF(SET_INT, managesieve_max_line_length),
+
 	{ 0, NULL, 0 }
 };
 
@@ -366,6 +369,9 @@
 	MEMBER(pop3_client_workarounds) "",
 	MEMBER(pop3_logout_format) "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s",
 
+	/* managesieve */
+	MEMBER(managesieve_max_line_length) 65536,
+
 	/* .. */
 	MEMBER(listen_fd) -1,
 	MEMBER(ssl_listen_fd) -1
@@ -499,6 +505,8 @@
 			auth->parent->pop3->ssl_verify_client_cert = TRUE;
 		if (auth->parent->imap != NULL)
 			auth->parent->imap->ssl_verify_client_cert = TRUE;
+		if (auth->parent->managesieve != NULL)
+			auth->parent->managesieve->ssl_verify_client_cert = TRUE;
 	}
 
 	return TRUE;
@@ -540,9 +548,12 @@
 	if (set->protocol == MAIL_PROTOCOL_IMAP) {
 		if (strstr(set->protocols, "imap") == NULL)
 			return FALSE;
-	} else {
+	} else if (set->protocol == MAIL_PROTOCOL_POP3) {
 		if (strstr(set->protocols, "pop3") == NULL)
 			return FALSE;
+	} else {
+		if (strstr(set->protocols, "managesieve") == NULL)
+			return FALSE;
 	}
 
 	return TRUE;
@@ -1043,6 +1054,15 @@
 							key, value);
 		}
 
+		if (error == NULL &&
+		    (ctx->protocol == MAIL_PROTOCOL_ANY ||
+		     ctx->protocol == MAIL_PROTOCOL_MANAGESIEVE)) {
+			error = parse_setting_from_defs(settings_pool,
+							setting_defs,
+							ctx->server->managesieve,
+							key, value);
+		}
+
 		if (error == NULL)
 			return NULL;
 
@@ -1101,6 +1121,13 @@
 			array_append(&ctx->server->pop3->plugin_envs,
 				     &value, 1);
 		}
+		if (ctx->protocol == MAIL_PROTOCOL_ANY ||
+		    ctx->protocol == MAIL_PROTOCOL_MANAGESIEVE) {
+			array_append(&ctx->server->managesieve->plugin_envs, &key, 1);
+			array_append(&ctx->server->managesieve->plugin_envs,
+				     &value, 1);
+		}
+
 		return NULL;
 	}
 
@@ -1110,7 +1137,8 @@
 static struct server_settings *
 create_new_server(const char *name,
 		  struct settings *imap_defaults,
-		  struct settings *pop3_defaults)
+		  struct settings *pop3_defaults,
+      struct settings *managesieve_defaults)
 {
 	struct server_settings *server;
 
@@ -1118,16 +1146,20 @@
 	server->name = p_strdup(settings_pool, name);
 	server->imap = p_new(settings_pool, struct settings, 1);
 	server->pop3 = p_new(settings_pool, struct settings, 1);
+	server->managesieve = p_new(settings_pool, struct settings, 1);
 	server->auth_defaults = default_auth_settings;
 
 	*server->imap = *imap_defaults;
 	*server->pop3 = *pop3_defaults;
+	*server->managesieve = *managesieve_defaults;
 
 	ARRAY_CREATE(&server->dicts, settings_pool, const char *, 4);
 	ARRAY_CREATE(&server->imap->plugin_envs, settings_pool,
 		     const char *, 8);
 	ARRAY_CREATE(&server->pop3->plugin_envs, settings_pool,
 		     const char *, 8);
+	ARRAY_CREATE(&server->managesieve->plugin_envs, settings_pool,
+         const char *, 8);
 
 	server->imap->server = server;
 	server->imap->protocol = MAIL_PROTOCOL_IMAP;
@@ -1141,6 +1173,12 @@
 	server->pop3->mail_executable = PKG_LIBEXECDIR"/pop3";
 	server->pop3->mail_plugin_dir = MODULEDIR"/pop3";
 
+	server->managesieve->server = server;
+	server->managesieve->protocol = MAIL_PROTOCOL_MANAGESIEVE;
+	server->managesieve->login_executable = PKG_LIBEXECDIR"/managesieve-login";
+	server->managesieve->mail_executable = PKG_LIBEXECDIR"/managesieve";
+	server->managesieve->mail_plugin_dir = MODULEDIR"/managesieve";
+  
 	return server;
 }
 
@@ -1184,8 +1222,8 @@
 
 		ctx->type = SETTINGS_TYPE_SERVER;
 		ctx->server = create_new_server(name, ctx->server->imap,
-						ctx->server->pop3);
-                server = ctx->root;
+						ctx->server->pop3, ctx->server->managesieve);
+		server = ctx->root;
 		while (server->next != NULL)
 			server = server->next;
 		server->next = ctx->server;
@@ -1206,6 +1244,8 @@
 			ctx->protocol = MAIL_PROTOCOL_POP3;
 		else if (strcmp(name, "lda") == 0)
 			ctx->protocol = MAIL_PROTOCOL_LDA;
+		else if (strcmp(name, "managesieve") == 0)
+			ctx->protocol = MAIL_PROTOCOL_MANAGESIEVE;
 		else {
 			*errormsg = "Unknown protocol name";
 			return FALSE;
@@ -1314,7 +1354,7 @@
 	ctx.protocol = MAIL_PROTOCOL_ANY;
 	ctx.server = ctx.root =
 		create_new_server("default",
-				  &default_settings, &default_settings);
+				  &default_settings, &default_settings, &default_settings);
 	ctx.auth = &ctx.server->auth_defaults;
 
 	if (!settings_read(path, NULL, parse_setting, parse_section, &ctx))
@@ -1332,7 +1372,8 @@
 	prev = NULL;
 	for (server = ctx.root; server != NULL; server = server->next) {
 		if ((*server->imap->protocols == '\0' ||
-		     *server->pop3->protocols == '\0') && !nochecks) {
+		     *server->pop3->protocols == '\0' ||
+		     *server->managesieve->protocols == '\0') && !nochecks) {
 			i_error("No protocols given in configuration file");
 			return FALSE;
 		}
@@ -1353,6 +1394,15 @@
 				server->defaults = server->pop3;
 		}
 
+		if (!settings_is_active(server->managesieve))
+			server->managesieve = NULL;
+		else {
+			if (!nochecks && !settings_verify(server->managesieve))
+				return FALSE;
+			if (server->defaults == NULL)
+				server->defaults = server->managesieve;
+		}
+
 		if (server->defaults == NULL) {
 			if (prev == NULL)
 				ctx.root = server->next;
@@ -1450,7 +1500,7 @@
 				       set_names[i], str[i]);
 			}
 		}
-		t_pop();
+    t_pop();
 	}
 }
 
@@ -1550,6 +1600,11 @@
 		sets[count] = set->pop3;
 		set_names[count] = "pop3";
 		count++;
+	}	
+	if (set->managesieve != NULL) {
+		sets[count] = set->managesieve;
+		set_names[count] = "managesieve";
+		count++;
 	}
 	settings_dump(setting_defs, sets, set_names, count, nondefaults, 0);
 	namespace_settings_dump(set->namespaces, nondefaults);
diff -r -u -N --exclude=Makefile.in old/src/master/master-settings.h new/src/master/master-settings.h
--- old/src/master/master-settings.h	2006-06-17 13:33:26.000000000 +0200
+++ new/src/master/master-settings.h	2006-07-13 12:57:22.610643672 +0200
@@ -4,10 +4,11 @@
 #include "network.h"
 
 enum mail_protocol {
-        MAIL_PROTOCOL_ANY,
-        MAIL_PROTOCOL_IMAP,
+	MAIL_PROTOCOL_ANY,
+	MAIL_PROTOCOL_IMAP,
 	MAIL_PROTOCOL_POP3,
-	MAIL_PROTOCOL_LDA
+	MAIL_PROTOCOL_LDA,
+	MAIL_PROTOCOL_MANAGESIEVE
 };
 
 struct settings {
@@ -115,6 +116,9 @@
 	const char *pop3_client_workarounds;
 	const char *pop3_logout_format;
 
+	/* managesieve */
+	unsigned int managesieve_max_line_length;
+
 	/* .. */
 	int listen_fd, ssl_listen_fd;
 
@@ -219,9 +223,10 @@
 	struct settings *defaults;
 	struct settings *imap;
 	struct settings *pop3;
+	struct settings *managesieve;
 	struct auth_settings *auths;
 	struct auth_settings auth_defaults;
-        struct namespace_settings *namespaces;
+	struct namespace_settings *namespaces;
 
 	array_t ARRAY_DEFINE(dicts, const char *);
 
