diff --git a/TODO b/TODO
index c2b253bb95b4a88f96533c782fc07c4b8bc96549..16665916a2222612612d802b82b07b2c608167f5 100644
--- a/TODO
+++ b/TODO
@@ -12,10 +12,9 @@ Next (in order of descending priority/precedence):
 * Code cleanup 
 * Full security review. Enforce limits on number of created objects, script 
   size, execution time, etc...
-	- Limit the string size
-	- Limit the string list size
 	- Limit the depth of the AST, i.e. command block and test list
 	  nesting.
+	- Make sure error messages don't print large erroneous values.
 	- Limit the maximum number of included scripts
 	- Make (configurable) limit on the number of redirects
 	- Implement limits recommended by the variables RFC (5229)
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 67bacbce09aa034931da4762c33cf632e5e3e3e7..be5d923c8a9f6e6b0a74d39dfe9a2195e334ead5 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -89,6 +89,7 @@ libsieve_la_SOURCES = \
 noinst_HEADERS = \
 	sieve-types.h \
 	sieve-common.h \
+	sieve-limits.h \
 	sieve-message.h \
 	sieve-lexer.h \
 	sieve-script.h \
diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c
index 08a882c721bf1ee59ef558a2a4589f031cf39e5a..5dbd3cab7821f6fa7406030e4fb9f228f8a64802 100644
--- a/src/lib-sieve/cmd-redirect.c
+++ b/src/lib-sieve/cmd-redirect.c
@@ -132,7 +132,7 @@ static bool cmd_redirect_validate
 			if ( norm_address == NULL ) {
 				sieve_command_validate_error(validator, cmd, 
 					"specified redirect address '%s' is invalid: %s",
-					str_c(address), error);
+					str_sanitize(str_c(address),256), error);
 			} else {
 				/* Replace string literal in AST */
 				sieve_ast_argument_string_setc(arg, norm_address);
diff --git a/src/lib-sieve/sieve-address.c b/src/lib-sieve/sieve-address.c
index 063f4a3bcd4c1592952eeaa546bb887225f243cd..a4f1b74299cb533ef77b4358e64a94b348d424af 100644
--- a/src/lib-sieve/sieve-address.c
+++ b/src/lib-sieve/sieve-address.c
@@ -1,5 +1,6 @@
 #include "lib.h"
 #include "str.h"
+#include "str-sanitize.h"
 #include "rfc822-parser.h"
 
 #include "sieve-common.h"
@@ -132,7 +133,7 @@ static int parse_addr_spec(struct sieve_message_address_parser *ctx)
 	} 
 
 	sieve_address_error(ctx, "invalid or lonely local part '%s' (expecting '@')", 
-		str_c(ctx->local_part));
+		str_sanitize(str_c(ctx->local_part), 80));
 	return -1;
 }
 
diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c
index 6f3a48f8363396f1a151c6bb7fc0d28dd00ed288..d21653f38cea279c7fc8afe7fa80a37f53b8a9cf 100644
--- a/src/lib-sieve/sieve-ast.c
+++ b/src/lib-sieve/sieve-ast.c
@@ -173,6 +173,9 @@ void sieve_ast_error
 	}
 
 #define __LIST_ADD(list, node) { \
+		if ( list->len + 1 < list->len ) \
+			return FALSE; \
+		\
 		node->next = NULL; \
 		if ( list->head == NULL ) { \
 			node->prev = NULL; \
@@ -185,9 +188,13 @@ void sieve_ast_error
 		} \
 		list->len++; \
 		node->list = list; \
+		return TRUE; \
 	}	 
 	
 #define __LIST_INSERT(list, before, node) { \
+		if ( list->len + 1 < list->len ) \
+			return FALSE; \
+		\
 		node->next = before; \
 		if ( list->head == before ) { \
 			node->prev = NULL; \
@@ -199,6 +206,8 @@ void sieve_ast_error
 		before->prev = node; \
 		list->len++; \
 		node->list = list; \
+		\
+		return TRUE; \
 	}	 
 
 	
@@ -206,7 +215,7 @@ void sieve_ast_error
 static struct sieve_ast_list *sieve_ast_list_create(pool_t pool) 
 	__LIST_CREATE(pool, struct sieve_ast_list)
 
-static void sieve_ast_list_add
+static bool sieve_ast_list_add
 (struct sieve_ast_list *list, struct sieve_ast_node *node) 
 	__LIST_ADD(list, node)
 
@@ -214,11 +223,11 @@ static void sieve_ast_list_add
 struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool) 
 	__LIST_CREATE(pool, struct sieve_ast_arg_list)
 	
-void sieve_ast_arg_list_add
+bool sieve_ast_arg_list_add
 	(struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument)
 	__LIST_ADD(list, argument)
 
-void sieve_ast_arg_list_insert
+bool sieve_ast_arg_list_insert
 	(struct sieve_ast_arg_list *list, struct sieve_ast_argument *before,
 	struct sieve_ast_argument *argument)
 	__LIST_INSERT(list, before, argument)
@@ -313,34 +322,34 @@ static struct sieve_ast_node *sieve_ast_node_create
 	return node;
 }
 
-static void sieve_ast_node_add_command
+static bool sieve_ast_node_add_command
 	(struct sieve_ast_node *node, struct sieve_ast_node *command) 
 {
 	i_assert( command->type == SAT_COMMAND && (node->type == SAT_ROOT || node->type == SAT_COMMAND) );
 	
 	if (node->commands == NULL) node->commands = sieve_ast_list_create(node->ast->pool);
 	
-	sieve_ast_list_add(node->commands, command);
+	return sieve_ast_list_add(node->commands, command);
 }
 
-static void sieve_ast_node_add_test
+static bool sieve_ast_node_add_test
 	(struct sieve_ast_node *node, struct sieve_ast_node *test) 
 {
 	i_assert( test->type == SAT_TEST && (node->type == SAT_TEST || node->type == SAT_COMMAND) );
 	
 	if (node->tests == NULL) node->tests = sieve_ast_list_create(node->ast->pool);
 	
-	sieve_ast_list_add(node->tests, test);
+	return sieve_ast_list_add(node->tests, test);
 }
 
-static void sieve_ast_node_add_argument
+static bool sieve_ast_node_add_argument
 	(struct sieve_ast_node *node, struct sieve_ast_argument *argument) 
 {
 	i_assert( node->type == SAT_TEST || node->type == SAT_COMMAND );
 	
 	if (node->arguments == NULL) node->arguments = sieve_ast_arg_list_create(node->ast->pool);
 	
-	sieve_ast_arg_list_add(node->arguments, argument);
+	return sieve_ast_arg_list_add(node->arguments, argument);
 }
 
 static void sieve_ast_argument_substitute
@@ -431,7 +440,7 @@ struct sieve_ast_argument *sieve_ast_argument_stringlist_substitute
 	return argument;
 }
 
-static void _sieve_ast_stringlist_add
+static bool _sieve_ast_stringlist_add
 	(struct sieve_ast_argument *list, string_t *str, unsigned int source_line) 
 {
 	struct sieve_ast_argument *stritem;
@@ -448,25 +457,25 @@ static void _sieve_ast_stringlist_add
 	/* Clone string */
 	stritem->_value.str = str;
 
-	sieve_ast_arg_list_add(list->_value.strlist, stritem);
+	return sieve_ast_arg_list_add(list->_value.strlist, stritem);
 }
 
-void sieve_ast_stringlist_add
+bool sieve_ast_stringlist_add
 	(struct sieve_ast_argument *list, const string_t *str, unsigned int source_line) 
 {
 	string_t *copied_str = str_new(list->ast->pool, str_len(str));
 	str_append_str(copied_str, str);
 
-	_sieve_ast_stringlist_add(list, copied_str, source_line);
+	return _sieve_ast_stringlist_add(list, copied_str, source_line);
 }
 
-void sieve_ast_stringlist_add_strc
+bool sieve_ast_stringlist_add_strc
 	(struct sieve_ast_argument *list, const char *str, unsigned int source_line) 
 {
 	string_t *copied_str = str_new(list->ast->pool, strlen(str));
 	str_append(copied_str, str);
 	
-	_sieve_ast_stringlist_add(list, copied_str, source_line);
+	return _sieve_ast_stringlist_add(list, copied_str, source_line);
 }
 
 struct sieve_ast_argument *sieve_ast_argument_tag_create
@@ -478,7 +487,8 @@ struct sieve_ast_argument *sieve_ast_argument_tag_create
 	argument->type = SAAT_TAG;
 	argument->_value.tag = p_strdup(node->ast->pool, tag);
 
-	sieve_ast_node_add_argument(node, argument);
+	if ( !sieve_ast_node_add_argument(node, argument) )
+		return NULL;
 
 	return argument;
 }
@@ -492,7 +502,8 @@ struct sieve_ast_argument *sieve_ast_argument_tag_insert
 	argument->type = SAAT_TAG;
 	argument->_value.tag = p_strdup(before->ast->pool, tag);
 
-	sieve_ast_arg_list_insert(before->list, before, argument);
+	if ( !sieve_ast_arg_list_insert(before->list, before, argument) )
+		return NULL;
 	
 	return argument;
 }
@@ -507,7 +518,8 @@ struct sieve_ast_argument *sieve_ast_argument_number_create
 	argument->type = SAAT_NUMBER;
 	argument->_value.number = number;
 	
-	sieve_ast_node_add_argument(node, argument);
+	if ( !sieve_ast_node_add_argument(node, argument) )
+		return NULL;
 	
 	return argument;
 }
@@ -541,7 +553,8 @@ struct sieve_ast_node *sieve_ast_test_create
 		
 	test->identifier = p_strdup(parent->ast->pool, identifier);
 	
-	sieve_ast_node_add_test(parent, test);
+	if ( !sieve_ast_node_add_test(parent, test) )
+		return NULL;
 	
 	return test;
 }
@@ -556,7 +569,8 @@ struct sieve_ast_node *sieve_ast_command_create
 	
 	command->identifier = p_strdup(parent->ast->pool, identifier);
 	
-	sieve_ast_node_add_command(parent, command);
+	if ( !sieve_ast_node_add_command(parent, command) )
+		return NULL;
 	
 	return command;
 }
diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h
index 36c5d3cca8ff4b351ac969ae05189725deddff8d..b96261258c1799af53324b842cbc77d57e9cf4bd 100644
--- a/src/lib-sieve/sieve-ast.h
+++ b/src/lib-sieve/sieve-ast.h
@@ -185,9 +185,9 @@ struct sieve_ast_argument *sieve_ast_argument_create
 	(struct sieve_ast *ast, unsigned int source_line);
 
 struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool);	
-void sieve_ast_arg_list_add
+bool sieve_ast_arg_list_add
 	(struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument);
-void sieve_ast_arg_list_insert
+bool sieve_ast_arg_list_insert
 	(struct sieve_ast_arg_list *list, struct sieve_ast_argument *before,
 		struct sieve_ast_argument *argument);
 void sieve_ast_arg_list_substitute
@@ -221,10 +221,10 @@ const char *sieve_ast_argument_type_name(enum sieve_ast_argument_type arg_type);
 #define sieve_ast_argument_name(argument) \
 	sieve_ast_argument_type_name((argument)->type)
 
-void sieve_ast_stringlist_add
+bool sieve_ast_stringlist_add
 	(struct sieve_ast_argument *list, const string_t *str, 
 		unsigned int source_line);
-void sieve_ast_stringlist_add_strc
+bool sieve_ast_stringlist_add_strc
 	(struct sieve_ast_argument *list, const char *str, 
 		unsigned int source_line);
 
diff --git a/src/lib-sieve/sieve-code.c b/src/lib-sieve/sieve-code.c
index 7ce43a0583a2c385efd3644879cc0e15e7999318..254f2ea9dc4361f32d27b7b95cd224d4a3c35798 100644
--- a/src/lib-sieve/sieve-code.c
+++ b/src/lib-sieve/sieve-code.c
@@ -415,7 +415,7 @@ static bool opr_number_dump
 	sieve_size_t number = 0;
 	
 	if (sieve_binary_read_integer(denv->sbin, address, &number) ) {
-		sieve_code_dumpf(denv, "NUM: %ld", (long) number);
+		sieve_code_dumpf(denv, "NUM: %llu", (long long) number);
 
 		return TRUE;
 	}
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index a0ae3f5476d9be51568c25714ee902e36b57e5dc..55af27a8a9e721c4cf630b0600559367da72178c 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -9,8 +9,12 @@
 /* 
  * Types
  */
- 
-typedef uint64_t sieve_number_t;
+
+typedef size_t        sieve_size_t; 
+typedef uint32_t      sieve_offset_t;
+typedef uint32_t      sieve_number_t;
+
+#define SIEVE_MAX_NUMBER ((sieve_number_t) -1)
 
 /*
  * Predeclarations
@@ -71,7 +75,6 @@ struct sieve_operation;
 struct sieve_coded_stringlist;
 
 /* sieve-binary.h */
-typedef size_t sieve_size_t;
 struct sieve_binary;
 
 /* sieve-objects.h */
diff --git a/src/lib-sieve/sieve-lexer.c b/src/lib-sieve/sieve-lexer.c
index 4fa900b16fd49416257605b25ad60e75875cb3d6..b7be61e30404944948a5b4b84418b346663e4015 100644
--- a/src/lib-sieve/sieve-lexer.c
+++ b/src/lib-sieve/sieve-lexer.c
@@ -7,6 +7,7 @@
 #include "istream.h"
 
 #include "sieve-common.h"
+#include "sieve-limits.h"
 #include "sieve-error.h"
 #include "sieve-script.h"
 
@@ -290,7 +291,7 @@ static inline int sieve_lexer_curchar(struct sieve_lexer *lexer)
  */
 static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) 
 {
-	int start_line;
+	sieve_number_t start_line;
 	string_t *str;
 
 	/* Read first character */
@@ -405,12 +406,24 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer)
 			if ( sieve_lexer_curchar(lexer) == '\\' ) {
 				sieve_lexer_shift(lexer);
 			}
-		
-			str_append_c(str, sieve_lexer_curchar(lexer));	
+
+			if ( str_len(str) <= SIEVE_MAX_STRING_LEN ) 
+				str_append_c(str, sieve_lexer_curchar(lexer));
+
 			sieve_lexer_shift(lexer);
 		}
-		
+
 		sieve_lexer_shift(lexer);
+
+		if ( str_len(str) > SIEVE_MAX_STRING_LEN ) {
+			sieve_lexer_error(lexer, 
+				"quoted string started at line %d is too long "
+				"(longer than %llu bytes)", start_line,
+				(long long) SIEVE_MAX_STRING_LEN);
+			lexer->token_type = STT_ERROR;
+			return FALSE;
+		}
+		
 		lexer->token_type = STT_STRING;
 		return TRUE;
 		
@@ -456,34 +469,61 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer)
 	default: 
 		/* number */
 		if ( IS_DIGIT(sieve_lexer_curchar(lexer)) ) {
-			int value = DIGIT_VAL(sieve_lexer_curchar(lexer));
+			sieve_number_t value = DIGIT_VAL(sieve_lexer_curchar(lexer));
+			bool overflow = FALSE;
+
 			sieve_lexer_shift(lexer);
   		
 			while ( IS_DIGIT(sieve_lexer_curchar(lexer)) ) {
-				value = value * 10 + DIGIT_VAL(sieve_lexer_curchar(lexer));
+				sieve_number_t valnew = 
+					value * 10 + DIGIT_VAL(sieve_lexer_curchar(lexer));
+			
+				/* Check for integer wrap */
+				if ( valnew < value )
+					overflow = TRUE;
+
+				value = valnew;
 				sieve_lexer_shift(lexer);
  			}
   		
 			switch ( sieve_lexer_curchar(lexer) ) { 
 			case 'k':
 			case 'K': /* Kilo */
-				value *= 1024;
+				if ( value > (SIEVE_MAX_NUMBER >> 10) )
+					overflow = TRUE;
+				else
+					value = value << 10;
 				sieve_lexer_shift(lexer);
 				break;
 			case 'm': 
 			case 'M': /* Mega */
-				value *= 1024*1024;
+				if ( value > (SIEVE_MAX_NUMBER >> 20) )
+					overflow = TRUE;
+				else
+					value = value << 20;
 				sieve_lexer_shift(lexer);
 				break;
 			case 'g':
 			case 'G': /* Giga */
-				value *= 1024*1024*1024;
+				if ( value > (SIEVE_MAX_NUMBER >> 30) )
+					overflow = TRUE;
+				else
+					value = value << 30;
 				sieve_lexer_shift(lexer);
 				break;
 			default:
 				/* Next token */
 				break;
 			}
+
+			/* Check for integer wrap */
+			if ( overflow ) {
+				sieve_lexer_error(lexer,
+					"number exceeds integer limits (max %llu)",
+					(long long) SIEVE_MAX_NUMBER);
+				lexer->token_type = STT_ERROR;
+				return FALSE;
+			}
   	
 			lexer->token_type = STT_NUMBER;
 			lexer->token_int_value = value;
@@ -531,6 +571,8 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer)
 				type == STT_IDENTIFIER && str_len(str) == 4 &&
 				strncasecmp(str_c(str), "text", 4) == 0 ) {
 				sieve_lexer_shift(lexer); // discard colon
+
+				start_line = lexer->current_line;
   			
 				/* Discard SP and HTAB whitespace */
 				while ( sieve_lexer_curchar(lexer) == ' ' || 
@@ -581,6 +623,16 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer)
   				
 						if ( sieve_lexer_curchar(lexer) == '\n' ) {
 							sieve_lexer_shift(lexer);
+
+							if ( str_len(str) > SIEVE_MAX_STRING_LEN ) {
+								sieve_lexer_error(lexer, 
+									"literal string started at line %d is too long "
+									"(longer than %llu bytes)", start_line,
+									(long long) SIEVE_MAX_STRING_LEN);
+									lexer->token_type = STT_ERROR;
+									return FALSE;
+							}
+
 							lexer->token_type = STT_STRING;
 							return TRUE;
 						} else if ( cr_shifted ) {
@@ -591,7 +643,8 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer)
 						}
 
 						/* Handle dot-stuffing */
-						str_append_c(str, '.');
+						if ( str_len(str) <= SIEVE_MAX_STRING_LEN ) 
+							str_append_c(str, '.');
 						if ( sieve_lexer_curchar(lexer) == '.' ) 
 	                        sieve_lexer_shift(lexer);
 					}
@@ -604,11 +657,16 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer)
  							lexer->token_type = STT_ERROR;
  							return FALSE;
  						}
-  					
-						str_append_c(str, sieve_lexer_curchar(lexer));
+						
+						if ( str_len(str) <= SIEVE_MAX_STRING_LEN ) 
+  							str_append_c(str, sieve_lexer_curchar(lexer));
+
 						sieve_lexer_shift(lexer);
 					}
-					str_append_c(str, '\n');
+
+					if ( str_len(str) <= SIEVE_MAX_STRING_LEN ) 
+						str_append_c(str, '\n');
+
 					sieve_lexer_shift(lexer);
 				}
   			
diff --git a/src/lib-sieve/sieve-limits.h b/src/lib-sieve/sieve-limits.h
new file mode 100644
index 0000000000000000000000000000000000000000..fea09d8d7b433b7de22210022e8f6d9d191030be
--- /dev/null
+++ b/src/lib-sieve/sieve-limits.h
@@ -0,0 +1,24 @@
+#ifndef __SIEVE_LIMITS_H
+#define __SIEVE_LIMITS_H
+
+/*
+ * Lexer
+ */
+
+#define SIEVE_MAX_STRING_LEN        (1 << 20)
+
+/*
+ * AST
+ */
+
+#define SIEVE_MAX_BLOCK_NESTING     32
+#define SIEVE_MAX_TEST_NESTING      32
+
+/*
+ * Actions
+ */
+
+#define SIEVE_DEFAULT_MAX_REDIRECTS 4
+#define SIEVE_DEFAULT_MAX_ACTIONS   32
+
+#endif
diff --git a/src/lib-sieve/sieve-parser.c b/src/lib-sieve/sieve-parser.c
index a33d89d8d0e3f975238a07e6e86efc2be4aa0e4d..84306fa737ee53d07874e6a126d9dcce717099a0 100644
--- a/src/lib-sieve/sieve-parser.c
+++ b/src/lib-sieve/sieve-parser.c
@@ -26,7 +26,7 @@ inline static void sieve_parser_error
 inline static void sieve_parser_warning
 	(struct sieve_parser *parser, const char *fmt, ...) ATTR_FORMAT(2, 3); 
  
-static bool sieve_parser_recover
+static int sieve_parser_recover
 	(struct sieve_parser *parser, enum sieve_token_type end_token);
 
 /*
@@ -148,17 +148,18 @@ inline static void sieve_parser_warning
  *   test-list = "(" test *("," test) ")"
  *   test = identifier arguments
  */
-static bool sieve_parse_arguments
+static int sieve_parse_arguments
 (struct sieve_parser *parser, struct sieve_ast_node *node) 
 {	
 	struct sieve_lexer *lexer = parser->lexer;
 	struct sieve_ast_node *test = NULL;
-	bool argument = TRUE;
-	bool result = TRUE; /* Indicates whether the parser is in a defined, not 
+	bool test_present = TRUE;
+	bool arg_present = TRUE;
+	int result = TRUE; /* Indicates whether the parser is in a defined, not 
 	                       necessarily error-free state */
 	
 	/* Parse arguments */
-	while ( argument && result && 
+	while ( arg_present && result > 0 && 
 		(parser->valid || sieve_errors_more_allowed(parser->ehandler)) ) {
 		struct sieve_ast_argument *arg;
 		
@@ -169,27 +170,33 @@ static bool sieve_parse_arguments
 			/* Create stinglist object */
 			arg = sieve_ast_argument_stringlist_create
 				(node, sieve_lexer_current_line(parser->lexer));
+
+			if ( arg == NULL ) break;
 				
 			sieve_lexer_skip_token(lexer);			
 			
 			if ( sieve_lexer_current_token(lexer) == STT_STRING ) {
+				bool add_failed = FALSE;
+
 				/* Add the string to the list */
-				sieve_ast_stringlist_add
+				if ( !sieve_ast_stringlist_add
 					(arg, sieve_lexer_token_str(lexer), 
-						sieve_lexer_current_line(parser->lexer));
+						sieve_lexer_current_line(parser->lexer)) )
+					add_failed = TRUE;
 				
 				sieve_lexer_skip_token(lexer);
 				 
-				while ( sieve_lexer_current_token(lexer) == STT_COMMA &&
+				while ( !add_failed && sieve_lexer_current_token(lexer) == STT_COMMA &&
 					(parser->valid || sieve_errors_more_allowed(parser->ehandler)) ) {
 			
 					sieve_lexer_skip_token(lexer);
 				
 					if ( sieve_lexer_current_token(lexer) == STT_STRING ) {
 						/* Add the string to the list */
-						sieve_ast_stringlist_add
+						if ( !sieve_ast_stringlist_add
 							(arg, sieve_lexer_token_str(lexer), 
-								sieve_lexer_current_line(parser->lexer));
+								sieve_lexer_current_line(parser->lexer)) )
+							add_failed = TRUE;
 							
 						sieve_lexer_skip_token(lexer);
 					} else {
@@ -201,6 +208,12 @@ static bool sieve_parse_arguments
 						break;
 					}
 				}
+				
+				if ( add_failed ) {
+					sieve_parser_error(parser, 
+						"failed to accept more items in string list");
+					return -1;
+				}
 			} else {
 				sieve_parser_error(parser, 
 					"expecting string after '[' in string list, but found %s",
@@ -225,15 +238,16 @@ static bool sieve_parse_arguments
 			
 		/* Single string */
 		case STT_STRING: 
-			(void) sieve_ast_argument_string_create
+			arg = sieve_ast_argument_string_create
 				(node, sieve_lexer_token_str(lexer), 
 					sieve_lexer_current_line(parser->lexer));
+
 			sieve_lexer_skip_token(lexer);
 			break;
 		
 		/* Number */
 		case STT_NUMBER:
-			(void) sieve_ast_argument_number_create
+			arg = sieve_ast_argument_number_create
 				(node, sieve_lexer_token_int(lexer), 
 					sieve_lexer_current_line(parser->lexer));
 			sieve_lexer_skip_token(lexer);
@@ -241,7 +255,7 @@ static bool sieve_parse_arguments
 			
 		/* Tag */
 		case STT_TAG:
-			(void) sieve_ast_argument_tag_create
+			arg = sieve_ast_argument_tag_create
 				(node, sieve_lexer_token_ident(lexer), 
 					sieve_lexer_current_line(parser->lexer));
 			sieve_lexer_skip_token(lexer);
@@ -249,9 +263,15 @@ static bool sieve_parse_arguments
 			
 		/* End of argument list, continue with tests */
 		default:
-			argument = FALSE;
+			arg_present = FALSE;
 			break;
 		}
+
+		if ( arg_present && arg == NULL ) {
+			sieve_parser_error(parser, 
+				"failed to accept more arguments for command '%s'", node->identifier);
+			return -1;
+		}
 	}
 	
 	if ( !result ) return FALSE; /* Defer recovery to caller */
@@ -269,6 +289,9 @@ static bool sieve_parse_arguments
 				sieve_lexer_current_line(parser->lexer));
 		sieve_lexer_skip_token(lexer);
 		
+		/* Theoretically, test can be NULL */
+		if ( test == NULL ) break;
+
 		/* Parse test arguments, which may include more tests (recurse) */
 		if ( !sieve_parse_arguments(parser, test) ) {
 			return FALSE; /* Defer recovery to caller */
@@ -289,8 +312,10 @@ static bool sieve_parse_arguments
 					sieve_lexer_current_line(parser->lexer));
 			sieve_lexer_skip_token(lexer);
 		
+			if ( test == NULL ) break;
+
 			/* Parse test arguments, which may include more tests (recurse) */
-			if ( sieve_parse_arguments(parser, test) ) {
+			if ( (result=sieve_parse_arguments(parser, test)) > 0 ) {
 			
 				/* More tests ? */
 				while ( sieve_lexer_current_token(lexer) == STT_COMMA && 
@@ -303,9 +328,13 @@ static bool sieve_parse_arguments
 							(node, sieve_lexer_token_ident(lexer), 
 								sieve_lexer_current_line(parser->lexer));
 						sieve_lexer_skip_token(lexer);
+
+						if ( test == NULL ) break;
 						
 						/* Parse test arguments, which may include more tests (recurse) */
-						if ( !sieve_parse_arguments(parser, test) ) {
+						if ( (result=sieve_parse_arguments(parser, test)) <= 0 ) {
+							if ( result < 0 ) return result;
+
 							result = sieve_parser_recover(parser, STT_RBRACKET);
 							break;
 						}
@@ -318,8 +347,13 @@ static bool sieve_parse_arguments
 						break;
 					}
 				}
-			} else 
+
+				if ( test == NULL ) break;
+			} else { 
+				if ( result < 0 ) return result;
+
 				result = sieve_parser_recover(parser, STT_RBRACKET);
+			}
 		} else {
 			sieve_parser_error(parser, 
 				"expecting test identifier after '(' in test list, but found %s",
@@ -342,7 +376,7 @@ static bool sieve_parse_arguments
 			/* Recover function tries to make next token equal to ')'. If it succeeds 
 			 * we need to skip it.
 			 */
-			if ( (result = sieve_parser_recover(parser, STT_RBRACKET)) == TRUE ) 
+			if ( (result=sieve_parser_recover(parser, STT_RBRACKET)) == TRUE ) 
 				sieve_lexer_skip_token(lexer);
 		}
 		break;
@@ -351,8 +385,15 @@ static bool sieve_parse_arguments
 		/* Not an error: test / test-list is optional
 		 *   --> any errors are detected by the caller  
 		 */
+		test_present = FALSE;
 		break;
-	} 
+	}
+
+	if ( test_present && test == NULL ) {
+		sieve_parser_error(parser, 
+			"failed to accept more tests for command '%s'", node->identifier);
+		return -1;
+	}			
 	
 	return result;
 }
@@ -361,11 +402,11 @@ static bool sieve_parse_arguments
  * command = identifier arguments ( ";" / block )
  * block = "{" commands "}"
  */
-static bool sieve_parse_commands
+static int sieve_parse_commands
 (struct sieve_parser *parser, struct sieve_ast_node *block) 
 { 
 	struct sieve_lexer *lexer = parser->lexer;
-	bool result = TRUE;
+	int result = TRUE;
 
 	while ( sieve_lexer_current_token(lexer) == STT_IDENTIFIER && 
 		(parser->valid || sieve_errors_more_allowed(parser->ehandler)) ) {
@@ -374,6 +415,13 @@ static bool sieve_parse_commands
 				(block, sieve_lexer_token_ident(lexer), 
 					sieve_lexer_current_line(parser->lexer));
 	
+		if ( command == NULL ) {
+			sieve_parser_error(parser, 
+				"failed to accept more commands inside block of command '%s'", 
+				block->identifier);
+			return -1;
+		}
+
 		/* Defined state */
 		result = TRUE;		
 		
@@ -384,7 +432,7 @@ static bool sieve_parse_commands
 		/* Check whether the command is properly terminated 
 		 * (i.e. with ; or a new block) 
 		 */
-		if ( result &&
+		if ( result > 0 &&
 			sieve_lexer_current_token(lexer) != STT_SEMICOLON &&
 			sieve_lexer_current_token(lexer) != STT_LCURLY ) {
 			
@@ -396,12 +444,12 @@ static bool sieve_parse_commands
 		}
 		
 		/* Try to recover from parse errors to reacquire a defined state */
-		if ( !result ) {
+		if ( result == 0 ) {
 			result = sieve_parser_recover(parser, STT_SEMICOLON);
 		}
 		
 		/* Don't bother to continue if we are not in a defined state */
-		if ( !result ) return FALSE;
+		if ( result <= 0 ) return result;
 			
 		switch ( sieve_lexer_current_token(lexer) ) {
 		
@@ -415,7 +463,7 @@ static bool sieve_parse_commands
 			
 			command->block = TRUE;
 			
-			if ( sieve_parse_commands(parser, command) ) {
+			if ( (result=sieve_parse_commands(parser, command)) > 0 ) {
 			
 				if ( sieve_lexer_current_token(lexer) != STT_RCURLY ) {
 					sieve_parser_error(parser, 
@@ -424,8 +472,12 @@ static bool sieve_parse_commands
 					result = sieve_parser_recover(parser, STT_RCURLY);				
 				} else 
 					sieve_lexer_skip_token(lexer);
-			} else 	if ( (result = sieve_parser_recover(parser, STT_RCURLY)) == TRUE ) 
-				sieve_lexer_skip_token(lexer);
+			} else {
+				if ( result < 0 ) return result;
+
+				if ( (result=sieve_parser_recover(parser, STT_RCURLY)) == 0 ) 
+					sieve_lexer_skip_token(lexer);
+			}
 
 			break;
 			
@@ -456,7 +508,7 @@ bool sieve_parser_run
 	sieve_lexer_skip_token(parser->lexer);
 
 	/* Parse */
-	if ( sieve_parse_commands(parser, sieve_ast_root(parser->ast)) && 
+	if ( sieve_parse_commands(parser, sieve_ast_root(parser->ast)) > 0 && 
 		parser->valid ) {
 		 
 		/* Parsed right to EOF ? */
@@ -516,7 +568,7 @@ static inline enum sieve_grammatical_prio __get_token_priority
 	return SGP_OTHER;
 }
 
-static bool sieve_parser_recover
+static int sieve_parser_recover
 (struct sieve_parser *parser, enum sieve_token_type end_token) 
 {
 	/* The tokens that begin/end a specific block/command/list in order 
diff --git a/tests/compile/errors.svtest b/tests/compile/errors.svtest
index 028ce6b4025de849d253bc6e27eae797424dab15..d19de2d0ce1dcc1d474b06dfd7c9d719d937c70e 100644
--- a/tests/compile/errors.svtest
+++ b/tests/compile/errors.svtest
@@ -8,6 +8,20 @@ require "comparator-i;ascii-numeric";
  * tested commands, but we want to be thorough. 
  */
 
+/*
+ * Lexer errors
+ */
+
+test "Lexer errors (FIXME: count only)" {
+    if test_compile "errors/lexer.sieve" {
+        test_fail "compile should have failed.";
+    }
+
+    if not test_error :count "eq" :comparator "i;ascii-numeric" "8" {
+        test_fail "wrong number of errors reported";
+    }
+}
+
 /*
  * Header test
  */
diff --git a/tests/compile/errors/lexer.sieve b/tests/compile/errors/lexer.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..200e2a44fd163b1f48fc975392b24cbe4197e4f9
--- /dev/null
+++ b/tests/compile/errors/lexer.sieve
@@ -0,0 +1,54 @@
+/*
+ * Lexer tests
+ *
+ * Total errors: 7 (+1 = 8)
+ */ 
+# Number too large
+if size :under 4294967300 {
+	stop;
+}
+
+# Number too large
+if size :under 4294967296 {
+	stop;
+}
+
+# Number too large
+if size :over 35651584k {
+	stop;
+}
+
+# Number too large
+if size :over 34816M {
+	stop;
+}
+
+# Number too large
+if size :over 34G {
+	stop;
+}
+
+# Number too large
+if size :over 4G {
+	stop;
+}
+
+# Number far too large
+if size :over 49834598293485814273947921734981723971293741923 {
+	stop;
+}
+
+# Not an error
+if size :under 4294967295 {
+	stop;
+}
+
+# Not an error
+if size :under 4294967294 {
+	stop;
+}
+
+# Not an error
+if size :under 1G {
+	stop;
+}