dovecot-2.2: lib-imap: Added IMAP_PARSE_FLAG_SERVER_TEXT that fi...

dovecot at dovecot.org dovecot at dovecot.org
Wed Sep 26 18:01:29 EEST 2012


details:   http://hg.dovecot.org/dovecot-2.2/rev/382df961f290
changeset: 15129:382df961f290
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Sep 11 20:44:37 2012 +0300
description:
lib-imap: Added IMAP_PARSE_FLAG_SERVER_TEXT that fixes parsing input from IMAP server.

diffstat:

 src/lib-imap/imap-parser.c |  78 +++++++++++++++++++++++++++++++++++++++++++++-
 src/lib-imap/imap-parser.h |   6 ++-
 2 files changed, 82 insertions(+), 2 deletions(-)

diffs (160 lines):

diff -r 3e1f1b6887c8 -r 382df961f290 src/lib-imap/imap-parser.c
--- a/src/lib-imap/imap-parser.c	Tue Sep 11 19:14:09 2012 +0300
+++ b/src/lib-imap/imap-parser.c	Tue Sep 11 20:44:37 2012 +0300
@@ -17,7 +17,8 @@
 	ARG_PARSE_STRING,
 	ARG_PARSE_LITERAL,
 	ARG_PARSE_LITERAL_DATA,
-	ARG_PARSE_LITERAL_DATA_FORCED
+	ARG_PARSE_LITERAL_DATA_FORCED,
+	ARG_PARSE_TEXT
 };
 
 struct imap_parser {
@@ -37,6 +38,7 @@
 
 	enum arg_parse_type cur_type;
 	size_t cur_pos; /* parser position in input buffer */
+	bool cur_resp_text; /* we're parsing [resp-text-code] */
 
 	int str_first_escape; /* ARG_PARSE_STRING: index to first '\' */
 	uoff_t literal_size; /* ARG_PARSE_LITERAL: string size */
@@ -100,6 +102,7 @@
 
 	parser->cur_type = ARG_PARSE_NONE;
 	parser->cur_pos = 0;
+	parser->cur_resp_text = FALSE;
 
 	parser->str_first_escape = 0;
 	parser->literal_size = 0;
@@ -210,6 +213,7 @@
 
 	switch (parser->cur_type) {
 	case ARG_PARSE_ATOM:
+	case ARG_PARSE_TEXT:
 		if (size == 3 && memcmp(data, "NIL", 3) == 0) {
 			/* NIL argument */
 			arg->type = IMAP_ARG_NIL;
@@ -468,6 +472,56 @@
 	}
 }
 
+static bool imap_parser_is_next_resp_text(struct imap_parser *parser)
+{
+	const struct imap_arg *arg;
+
+	if (parser->cur_list != &parser->root_list ||
+	    array_count(parser->cur_list) != 1)
+		return FALSE;
+
+	arg = array_idx(&parser->root_list, 0);
+	if (arg->type != IMAP_ARG_ATOM)
+		return FALSE;
+
+	return strcasecmp(arg->_data.str, "OK") == 0 ||
+		strcasecmp(arg->_data.str, "NO") == 0 ||
+		strcasecmp(arg->_data.str, "BAD") == 0 ||
+		strcasecmp(arg->_data.str, "BYE") == 0;
+}
+
+static bool imap_parser_is_next_text(struct imap_parser *parser)
+{
+	const struct imap_arg *arg;
+	unsigned int len;
+
+	if (parser->cur_list != &parser->root_list)
+		return FALSE;
+
+	arg = array_idx(&parser->root_list, array_count(&parser->root_list)-1);
+	if (arg->type != IMAP_ARG_ATOM)
+		return FALSE;
+
+	len = strlen(arg->_data.str);
+	return len > 0 && arg->_data.str[len-1] == ']';
+}
+
+static bool imap_parser_read_text(struct imap_parser *parser,
+				  const unsigned char *data, size_t data_size)
+{
+	size_t i;
+
+	/* read until end of line */
+	for (i = parser->cur_pos; i < data_size; i++) {
+		if (is_linebreak(data[i])) {
+			imap_parser_save_arg(parser, data, i);
+			break;
+		}
+	}
+	parser->cur_pos = i;
+	return parser->cur_type == ARG_PARSE_NONE;
+}
+
 /* Returns TRUE if argument was fully processed. Also returns TRUE if
    an argument inside a list was processed. */
 static int imap_parser_read_arg(struct imap_parser *parser)
@@ -485,6 +539,13 @@
 			return FALSE;
 		i_assert(parser->cur_pos == 0);
 
+		if (parser->cur_resp_text &&
+		    imap_parser_is_next_text(parser)) {
+			/* we just parsed [resp-text-code] */
+			parser->cur_type = ARG_PARSE_TEXT;
+			break;
+		}
+
 		switch (data[0]) {
 		case '\r':
 			if (data_size == 1) {
@@ -538,6 +599,16 @@
 	case ARG_PARSE_ATOM:
 		if (!imap_parser_read_atom(parser, data, data_size))
 			return FALSE;
+		if ((parser->flags & IMAP_PARSE_FLAG_SERVER_TEXT) == 0)
+			break;
+
+		if (imap_parser_is_next_resp_text(parser)) {
+			/* we just parsed OK/NO/BAD/BYE. after parsing the
+			   [resp-text-code] the rest of the message can contain
+			   pretty much any random text, which we can't parse
+			   as if it was valid IMAP input */
+			parser->cur_resp_text = TRUE;
+		}
 		break;
 	case ARG_PARSE_STRING:
 		if (!imap_parser_read_string(parser, data, data_size))
@@ -557,6 +628,10 @@
 		if (!imap_parser_read_literal_data(parser, data, data_size))
 			return FALSE;
 		break;
+	case ARG_PARSE_TEXT:
+		if (!imap_parser_read_text(parser, data, data_size))
+			return FALSE;
+		break;
 	default:
                 i_unreached();
 	}
@@ -579,6 +654,7 @@
 	parser->line_size += parser->cur_pos;
 	i_stream_skip(parser->input, parser->cur_pos);
 	parser->cur_pos = 0;
+	parser->cur_resp_text = FALSE;
 
 	if (parser->list_arg != NULL && !parser->literal_size_return) {
 		parser->error = "Missing ')'";
diff -r 3e1f1b6887c8 -r 382df961f290 src/lib-imap/imap-parser.h
--- a/src/lib-imap/imap-parser.h	Tue Sep 11 19:14:09 2012 +0300
+++ b/src/lib-imap/imap-parser.h	Tue Sep 11 20:44:37 2012 +0300
@@ -16,7 +16,11 @@
 	/* Don't check if atom contains invalid characters */
 	IMAP_PARSE_FLAG_ATOM_ALLCHARS	= 0x08,
 	/* Allow strings to contain CRLFs */
-	IMAP_PARSE_FLAG_MULTILINE_STR	= 0x10
+	IMAP_PARSE_FLAG_MULTILINE_STR	= 0x10,
+	/* We're parsing IMAP server replies. Parse the "text" after
+	   OK/NO/BAD/BYE replies as a single atom. We assume that the initial
+	   "*" or tag was already skipped over. */
+	IMAP_PARSE_FLAG_SERVER_TEXT	= 0x20
 };
 
 struct imap_parser;


More information about the dovecot-cvs mailing list