changeset 1591:6eca99b727a0 HEAD

IMAP parser memory limits are now enforced by bytes per line rather than limiting maximum amount of tokens per line. Default is 64k now, which should help with the huge message sets generated by some clients.
author Timo Sirainen <tss@iki.fi>
date Wed, 02 Jul 2003 03:57:24 +0300
parents 6bf54ebc16af
children 8156b82727a8
files src/imap-login/client.c src/imap/client.c src/imap/cmd-append.c src/imap/common.h src/imap/main.c src/lib-imap/imap-bodystructure.c src/lib-imap/imap-envelope.c src/lib-imap/imap-parser.c src/lib-imap/imap-parser.h
diffstat 9 files changed, 54 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap-login/client.c	Tue Jul 01 22:17:02 2003 +0300
+++ b/src/imap-login/client.c	Wed Jul 02 03:57:24 2003 +0300
@@ -20,9 +20,8 @@
 
 #define MAX_OUTBUF_SIZE 1024
 
-/* max. number of IMAP argument elements to accept. The maximum memory usage
-   for command from user is around MAX_INBUF_SIZE * MAX_IMAP_ARG_ELEMENTS */
-#define MAX_IMAP_ARG_ELEMENTS 4
+/* maximum length for IMAP command line. */
+#define MAX_IMAP_LINE 8192
 
 /* Disconnect client after idling this many seconds */
 #define CLIENT_LOGIN_IDLE_TIMEOUT 60
@@ -64,8 +63,7 @@
 	client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
 					      FALSE);
 	client->parser = imap_parser_create(client->input, client->output,
-					    MAX_INBUF_SIZE,
-					    MAX_IMAP_ARG_ELEMENTS);
+					    MAX_IMAP_LINE);
 }
 
 /* Skip incoming data until newline is found,
--- a/src/imap/client.c	Tue Jul 01 22:17:02 2003 +0300
+++ b/src/imap/client.c	Wed Jul 02 03:57:24 2003 +0300
@@ -9,9 +9,6 @@
 
 #include <stdlib.h>
 
-/* max. size of one parameter in line */
-#define MAX_INBUF_SIZE 8192
-
 /* If we can't send a buffer in a minute, disconnect the client */
 #define CLIENT_OUTPUT_TIMEOUT (60*1000)
 
@@ -49,7 +46,7 @@
 
 	client = i_new(struct client, 1);
 	client->input = i_stream_create_file(hin, default_pool,
-					     MAX_INBUF_SIZE, FALSE);
+					     imap_max_line_length, FALSE);
 	client->output = o_stream_create_file(hout, default_pool, 4096, FALSE);
 
 	/* set timeout for reading expected data (eg. APPEND). This is
@@ -63,8 +60,7 @@
 
 	client->io = io_add(hin, IO_READ, _client_input, client);
 	client->parser = imap_parser_create(client->input, client->output,
-					    MAX_INBUF_SIZE,
-					    MAX_IMAP_ARG_ELEMENTS);
+					    imap_max_line_length);
         client->last_input = ioloop_time;
 
 	client->mailbox_flags.pool =
--- a/src/imap/cmd-append.c	Tue Jul 01 22:17:02 2003 +0300
+++ b/src/imap/cmd-append.c	Wed Jul 02 03:57:24 2003 +0300
@@ -95,7 +95,7 @@
 	count = 0;
 	failed = TRUE;
 	save_parser = imap_parser_create(client->input, client->output,
-					 0, MAX_IMAP_ARG_ELEMENTS);
+					 imap_max_line_length);
 
 	for (;;) {
 		/* [<flags>] [<internal date>] <message literal> */
--- a/src/imap/common.h	Tue Jul 01 22:17:02 2003 +0300
+++ b/src/imap/common.h	Wed Jul 02 03:57:24 2003 +0300
@@ -4,17 +4,19 @@
 #include "lib.h"
 #include "client.h"
 
-/* max. number of IMAP argument elements to accept. The maximum memory usage
-   for command from user is around MAX_INBUF_SIZE * MAX_IMAP_ARG_ELEMENTS */
-#define MAX_IMAP_ARG_ELEMENTS 128
-
 /* Disconnect client after idling this many seconds */
 #define CLIENT_IDLE_TIMEOUT (60*30)
 
+/* 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_IMAP_MAX_LINE_LENGTH 65536
+
 #define DEFAULT_MAX_CUSTOM_FLAG_LENGTH 50
 
 extern struct ioloop *ioloop;
 extern unsigned int max_custom_flag_length, mailbox_check_interval;
+extern unsigned int imap_max_line_length;
 
 extern string_t *capability_string;
 
--- a/src/imap/main.c	Tue Jul 01 22:17:02 2003 +0300
+++ b/src/imap/main.c	Wed Jul 02 03:57:24 2003 +0300
@@ -22,6 +22,7 @@
 
 struct ioloop *ioloop;
 unsigned int max_custom_flag_length, mailbox_check_interval;
+unsigned int imap_max_line_length;
 
 static struct module *modules;
 static char log_prefix[128]; /* syslog() needs this to be permanent */
@@ -127,6 +128,11 @@
 		}
 	}
 
+	str = getenv("IMAP_MAX_LINE_LENGTH");
+	imap_max_line_length = str != NULL ?
+		(unsigned int)strtoul(str, NULL, 10) :
+		DEFAULT_IMAP_MAX_LINE_LENGTH;
+
 	str = getenv("MAIL_MAX_FLAG_LENGTH");
 	max_custom_flag_length = str != NULL ?
 		(unsigned int)strtoul(str, NULL, 10) :
--- a/src/lib-imap/imap-bodystructure.c	Tue Jul 01 22:17:02 2003 +0300
+++ b/src/lib-imap/imap-bodystructure.c	Wed Jul 02 03:57:24 2003 +0300
@@ -679,7 +679,7 @@
 	input = i_stream_create_from_data(data_stack_pool, bodystructure, len);
 	(void)i_stream_read(input);
 
-	parser = imap_parser_create(input, NULL, 0, (size_t)-1);
+	parser = imap_parser_create(input, NULL, (size_t)-1);
 	ret = imap_parser_read_args(parser, 0, IMAP_PARSE_FLAG_NO_UNESCAPE |
 				    IMAP_PARSE_FLAG_LITERAL_TYPE, &args);
 
--- a/src/lib-imap/imap-envelope.c	Tue Jul 01 22:17:02 2003 +0300
+++ b/src/lib-imap/imap-envelope.c	Wed Jul 02 03:57:24 2003 +0300
@@ -368,7 +368,7 @@
 
 	input = i_stream_create_from_data(data_stack_pool, envelope,
 					  strlen(envelope));
-	parser = imap_parser_create(input, NULL, (size_t)-1, (size_t)-1);
+	parser = imap_parser_create(input, NULL, (size_t)-1);
 
 	(void)i_stream_read(input);
 	ret = imap_parser_read_args(parser, field+1, 0, &args);
--- a/src/lib-imap/imap-parser.c	Tue Jul 01 22:17:02 2003 +0300
+++ b/src/lib-imap/imap-parser.c	Wed Jul 02 03:57:24 2003 +0300
@@ -28,11 +28,12 @@
 	pool_t pool;
 	struct istream *input;
 	struct ostream *output;
-	size_t max_literal_size, max_elements;
+	size_t max_line_size;
         enum imap_parser_flags flags;
 
 	/* reset by imap_parser_reset(): */
-        struct imap_arg_list *root_list;
+	size_t line_size;
+	struct imap_arg_list *root_list;
         struct imap_arg_list *cur_list;
 	struct imap_arg *list_arg;
 	size_t element_count;
@@ -73,7 +74,7 @@
 
 struct imap_parser *
 imap_parser_create(struct istream *input, struct ostream *output,
-		   size_t max_literal_size, size_t max_elements)
+		   size_t max_line_size)
 {
 	struct imap_parser *parser;
 
@@ -81,8 +82,7 @@
         parser->pool = pool_alloconly_create("IMAP parser", 8192);
 	parser->input = input;
 	parser->output = output;
-	parser->max_literal_size = max_literal_size;
-	parser->max_elements = max_elements;
+	parser->max_line_size = max_line_size;
 
 	imap_args_realloc(parser, LIST_ALLOC_SIZE);
 	return parser;
@@ -98,6 +98,8 @@
 {
 	p_clear(parser->pool);
 
+	parser->line_size = 0;
+
 	parser->root_list = NULL;
 	parser->cur_list = NULL;
 	parser->list_arg = NULL;
@@ -136,6 +138,7 @@
 			break;
 	}
 
+	parser->line_size += i;
         i_stream_skip(parser->input, i);
 	parser->cur_pos = 0;
 
@@ -352,7 +355,9 @@
 static int imap_parser_literal_end(struct imap_parser *parser)
 {
 	if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_SIZE) == 0) {
-		if (parser->literal_size > parser->max_literal_size) {
+		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;
@@ -381,6 +386,7 @@
 	/* 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 imap_parser_literal_end(parser);
 		}
@@ -424,6 +430,7 @@
 			return FALSE;
 
 		if (*data == '\r') {
+			parser->line_size++;
 			data++; data_size--;
 			i_stream_skip(parser->input, 1);
 
@@ -436,8 +443,10 @@
 			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);
@@ -564,14 +573,15 @@
 		if (!imap_parser_read_arg(parser))
 			break;
 
-		if (parser->element_count > parser->max_elements) {
-			parser->error = "Too many argument elements";
+		if (parser->line_size > parser->max_line_size) {
+			parser->error = "IMAP 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;
@@ -579,6 +589,7 @@
 	} else if ((!IS_UNFINISHED(parser) && count > 0 &&
 		    parser->root_list->size >= count) || parser->eol) {
 		/* all arguments read / end of line. */
+		parser->line_size += parser->cur_pos;
  		i_stream_skip(parser->input, parser->cur_pos);
 		parser->cur_pos = 0;
 
@@ -621,27 +632,9 @@
 	}
 
 	if (i < data_size) {
-		i_stream_skip(parser->input, i + (data[i] == ' ' ? 1 : 0));
-		return p_strndup(parser->pool, data, i);
-	} else {
-		return NULL;
-	}
-}
-
-const char *imap_parser_read_line(struct imap_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] == '\r' || data[i] == '\n')
-			break;
-	}
-
-	if (i < data_size) {
-		i_stream_skip(parser->input, i);
+		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;
--- a/src/lib-imap/imap-parser.h	Tue Jul 01 22:17:02 2003 +0300
+++ b/src/lib-imap/imap-parser.h	Wed Jul 02 03:57:24 2003 +0300
@@ -60,15 +60,20 @@
 	struct imap_arg args[1]; /* variable size */
 };
 
-/* Create new IMAP argument parser. There's no limit in argument sizes, only
-   the maximum buffer size of input stream limits it. max_literal_size limits
-   the maximum size of internally handled literals (ie. FLAG_LITERAL_SIZE is
-   unset). max_elements sets the number of elements we allow entirely so that
-   user can't give huge lists or lists inside lists. output is used for sending
-   command continuation requests for literals. */
+/* Create new IMAP 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 imap_parser *
 imap_parser_create(struct istream *input, struct ostream *output,
-		   size_t max_literal_size, size_t max_elements);
+		   size_t max_line_size);
 void imap_parser_destroy(struct imap_parser *parser);
 
 /* Reset the parser to initial state. */
@@ -94,9 +99,6 @@
    Returns NULL if more data is needed. */
 const char *imap_parser_read_word(struct imap_parser *parser);
 
-/* Read the rest of the line. Returns NULL if more data is needed. */
-const char *imap_parser_read_line(struct imap_parser *parser);
-
 /* Returns the imap argument as string. NIL returns "" and list returns NULL. */
 const char *imap_arg_string(struct imap_arg *arg);