dovecot-2.1-pigeonhole: ManageSieve: added support for reading q...

pigeonhole at rename-it.nl pigeonhole at rename-it.nl
Sun Jan 22 22:57:54 EET 2012


details:   http://hg.rename-it.nl/dovecot-2.1-pigeonhole/rev/ae857042882b
changeset: 1596:ae857042882b
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Sun Jan 22 21:57:45 2012 +0100
description:
ManageSieve: added support for reading quoted and literal strings as a stream. Fixes support for handing large SASL responses.
Also resolves long-standing FIXME regarding the second parameter of PUTSCRIPT: it can now be a quoted string.
Includes a few small changes in the login daemon that were done in the dovecot equivalents before.

diffstat:

 src/lib-managesieve/managesieve-parser.c    |  226 ++++++++++++++++++++++++++++----
 src/lib-managesieve/managesieve-parser.h    |   54 ++++---
 src/lib-sievestorage/sieve-storage-quota.c  |    6 +
 src/lib-sievestorage/sieve-storage-quota.h  |    3 +
 src/managesieve-login/client-authenticate.c |  203 ++++++++++++++++++++++------
 src/managesieve-login/client.c              |  153 +++++++++++++--------
 src/managesieve-login/client.h              |    7 +
 src/managesieve-login/managesieve-proxy.c   |    2 +-
 src/managesieve/cmd-putscript.c             |  164 +++++++++++++----------
 src/managesieve/managesieve-client.c        |    2 +-
 src/managesieve/managesieve-quota.c         |    5 +
 src/managesieve/managesieve-quota.h         |    2 +
 12 files changed, 596 insertions(+), 231 deletions(-)

diffs (truncated from 1385 to 300 lines):

diff -r 66c9a4a82428 -r ae857042882b src/lib-managesieve/managesieve-parser.c
--- a/src/lib-managesieve/managesieve-parser.c	Fri Jan 20 22:26:53 2012 +0100
+++ b/src/lib-managesieve/managesieve-parser.c	Sun Jan 22 21:57:45 2012 +0100
@@ -3,7 +3,7 @@
 
 #include "lib.h"
 #include "unichar.h"
-#include "istream.h"
+#include "istream-private.h"
 #include "ostream.h"
 #include "strescape.h"
 #include "managesieve-parser.h"
@@ -25,7 +25,6 @@
 	/* permanent */
 	pool_t pool;
 	struct istream *input;
-	struct ostream *output;
 	size_t max_line_size;
 	enum managesieve_parser_flags flags;
 
@@ -40,6 +39,8 @@
 	int str_first_escape; /* ARG_PARSE_STRING: index to first '\' */
 	uoff_t literal_size; /* ARG_PARSE_LITERAL: string size */
 
+	struct istream *str_stream;
+
 	const char *error;
 
 	unsigned int literal_skip_crlf:1;
@@ -48,6 +49,9 @@
 	unsigned int fatal_error:1;
 };
 
+static struct istream *quoted_string_istream_create
+	(struct managesieve_parser *parser);
+
 /* @UNSAFE */
 #define LIST_REALLOC(parser, old_list, new_size) \
 	p_realloc((parser)->pool, old_list, \
@@ -65,15 +69,13 @@
 }
 
 struct managesieve_parser *
-managesieve_parser_create(struct istream *input, struct ostream *output,
-		   size_t max_line_size)
+managesieve_parser_create(struct istream *input, 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);
@@ -82,6 +84,9 @@
 
 void managesieve_parser_destroy(struct managesieve_parser **parser)
 {
+	if ((*parser)->str_stream != NULL)
+		i_stream_unref(&(*parser)->str_stream);
+
 	pool_unref(&(*parser)->pool);
 	i_free(*parser);
 	*parser = NULL;
@@ -107,10 +112,14 @@
 	parser->literal_skip_crlf = FALSE;
 	parser->eol = FALSE;
 
+	if ( parser->str_stream != NULL )
+		i_stream_unref(&parser->str_stream);
+
 	managesieve_args_realloc(parser, LIST_ALLOC_SIZE);
 }
 
-const char *managesieve_parser_get_error(struct managesieve_parser *parser, bool *fatal)
+const char *managesieve_parser_get_error
+(struct managesieve_parser *parser, bool *fatal)
 {
 	*fatal = parser->fatal_error;
 	return parser->error;
@@ -168,26 +177,29 @@
 		break;
 	case ARG_PARSE_STRING:
 		/* data is quoted and may contain escapes. */
-		i_assert(size > 0);
+		if ((parser->flags & MANAGESIEVE_PARSE_FLAG_STRING_STREAM) != 0) {
+			arg->type = MANAGESIEVE_ARG_STRING_STREAM;
+			arg->_data.str_stream = parser->str_stream;
+		} else {
+			i_assert(size > 0);
 
-		arg->type = MANAGESIEVE_ARG_STRING;
-		arg->_data.str = p_strndup(parser->pool, data+1, size-1);
+			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);
+			/* 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 = MANAGESIEVE_ARG_LITERAL_SIZE;
-			arg->_data.literal_size = parser->literal_size;
-		} else if ((parser->flags &
-			    MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE) != 0) {
+		if ((parser->flags & MANAGESIEVE_PARSE_FLAG_STRING_STREAM) != 0) {
+			arg->type = MANAGESIEVE_ARG_STRING_STREAM;
+			arg->_data.str_stream = parser->str_stream;
+		} 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 {
@@ -273,7 +285,8 @@
 			i++;
 
 			if ( !IS_QUOTED_SPECIAL(data[i]) ) {
-				parser->error = "Escaped quoted-string character is not a QUOTED-SPECIAL.";
+				parser->error =
+					"Escaped quoted-string character is not a QUOTED-SPECIAL.";
 				return FALSE;
 			}
 
@@ -292,7 +305,7 @@
 
 static int managesieve_parser_literal_end(struct managesieve_parser *parser)
 {
-	if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) == 0) {
+	if ((parser->flags & MANAGESIEVE_PARSE_FLAG_STRING_STREAM) == 0) {
 		if (parser->line_size >= parser->max_line_size ||
 		    parser->literal_size >
 		    	parser->max_line_size - parser->line_size) {
@@ -387,7 +400,7 @@
 		i_assert(parser->cur_pos == 0);
 	}
 
-	if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) == 0) {
+	if ((parser->flags & MANAGESIEVE_PARSE_FLAG_STRING_STREAM) == 0) {
 		/* now we just wait until we've read enough data */
 		if (data_size < parser->literal_size) {
 			return FALSE;
@@ -404,8 +417,10 @@
 			return TRUE;
 		}
 	} else {
-		/* we want to save only literal size, not the literal itself. */
+		/* we don't read the data; we just create a stream for the literal */
 		parser->eol = TRUE;
+		parser->str_stream = i_stream_create_limit
+			(parser->input, parser->literal_size);
 		managesieve_parser_save_arg(parser, NULL, 0);
 		return TRUE;
 	}
@@ -461,8 +476,17 @@
 			return FALSE;
 		break;
 	case ARG_PARSE_STRING:
-		if (!managesieve_parser_read_string(parser, data, data_size))
+		if ((parser->flags & MANAGESIEVE_PARSE_FLAG_STRING_STREAM) != 0) {
+			parser->eol = TRUE;
+			parser->line_size += parser->cur_pos;
+			i_stream_skip(parser->input, parser->cur_pos);
+			parser->cur_pos = 0;
+			parser->str_stream = quoted_string_istream_create(parser);
+			managesieve_parser_save_arg(parser, NULL, 0);
+
+		} else 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))
@@ -623,10 +647,10 @@
 	return NULL;
 }
 
-uoff_t _managesieve_arg_literal_size_error(const struct managesieve_arg *arg)
+struct istream *_managesieve_arg_str_stream_error(const struct managesieve_arg *arg)
 {
-	i_panic("Tried to access managesieve_arg type %d as literal size", arg->type);
-	return 0;
+	i_panic("Tried to access managesieve_arg type %d as string stream", arg->type);
+	return NULL;
 }
 
 struct managesieve_arg_list *_managesieve_arg_list_error(const struct managesieve_arg *arg)
@@ -634,3 +658,147 @@
 	i_panic("Tried to access managesieve_arg type %d as list", arg->type);
 	return NULL;
 }
+
+/*
+ * Quoted string stream
+ */
+
+struct quoted_string_istream {
+	struct istream_private istream;
+
+	struct stat statbuf;
+
+	struct managesieve_parser *parser;
+
+	unsigned int pending_slash:1;
+	unsigned int last_slash:1;
+	unsigned int finished:1;
+};
+
+static ssize_t quoted_string_istream_read(struct istream_private *stream)
+{
+	struct quoted_string_istream *qsstream =
+		(struct quoted_string_istream *)stream;
+	const unsigned char *data;
+	size_t i, dest, size;
+	ssize_t ret = 0;
+	bool slash;
+
+	if ( qsstream->finished ) {
+		stream->istream.eof = TRUE;
+		return -1;
+	}
+
+	/* Read from parent */
+	data = i_stream_get_data(stream->parent, &size);
+	if (size == 0) {
+		ret = i_stream_read(stream->parent);
+		if (ret <= 0 && (ret != -2 || stream->skip == 0)) {
+			if ( stream->istream.eof && stream->istream.stream_errno == 0 ) {
+				stream->istream.eof = 0;
+				stream->istream.stream_errno = EPROTO;
+			} else {
+				stream->istream.stream_errno = stream->parent->stream_errno;
+				stream->istream.eof = stream->parent->eof;
+			}
+			return ret;
+		}
+		data = i_stream_get_data(stream->parent, &size);
+		i_assert(size != 0);
+	}
+
+	/* Allocate buffer space */
+	if (!i_stream_get_buffer_space(stream, size, NULL))
+		return -2;
+
+	/* Parse quoted string content */
+	dest = stream->pos;
+	slash = qsstream->pending_slash;
+	ret = 0;
+	for (i = 0; i < size && dest < stream->buffer_size; i++) {
+		if ( data[i] == '"' ) {
+			if ( !slash ) {
+				qsstream->finished = TRUE;
+				i++;
+				break;
+			}
+			slash = FALSE;
+		} else if ( data[i] == '\\' ) {
+			if ( !slash ) {
+				slash = TRUE;
+				continue;
+			}
+			slash = FALSE;
+		} else if ( slash ) {
+			if ( !IS_QUOTED_SPECIAL(data[i]) ) {
+				qsstream->parser->error =
+					"Escaped quoted-string character is not a QUOTED-SPECIAL.";
+				stream->istream.stream_errno = EPROTO;
+				ret = -1;
+				break;
+			}
+			slash = FALSE;
+		}
+
+		if ( (data[i] & 0x80) == 0 && ( data[i] == '\r' || data[i] == '\n' ) ) {
+			qsstream->parser->error = "String contains invalid character.";
+			stream->istream.stream_errno = EPROTO;
+			ret = -1;
+			break;
+		}
+
+		stream->w_buffer[dest++] = data[i];
+	}
+
+	i_stream_skip(stream->parent, i);
+	qsstream->pending_slash = slash;
+
+	if ( ret < 0 ) {


More information about the dovecot-cvs mailing list