diff --git a/src/lib-sieve/sieve-lexer.c b/src/lib-sieve/sieve-lexer.c index 54aa08f0ca905cecd09b47c50186f75a157b8a86..93607573cd60bcdf08b7c079eb43430989e295a7 100644 --- a/src/lib-sieve/sieve-lexer.c +++ b/src/lib-sieve/sieve-lexer.c @@ -1,8 +1,12 @@ +/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file + */ + #include "lib.h" #include "compat.h" #include "str.h" #include "istream.h" +#include "sieve-common.h" #include "sieve-error.h" #include "sieve-script.h" @@ -15,11 +19,28 @@ #include <stdlib.h> #include <unistd.h> +/* + * Useful macros + */ + #define IS_DIGIT(c) ( c >= '0' && c <= '9' ) #define DIGIT_VAL(c) ( c - '0' ) #define IS_ALPHA(c) ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ) #define IS_QUANTIFIER(c) (c == 'K' || c == 'M' || c =='G') +/* + * Forward declarations + */ + +inline static void sieve_lexer_error + (struct sieve_lexer *lexer, const char *fmt, ...) ATTR_FORMAT(2, 3); +inline static void sieve_lexer_warning + (struct sieve_lexer *lexer, const char *fmt, ...) ATTR_FORMAT(2, 3); + +/* + * Lexer object + */ + struct sieve_lexer { pool_t pool; @@ -40,43 +61,6 @@ struct sieve_lexer { size_t buffer_pos; }; -inline static void sieve_lexer_error - (struct sieve_lexer *lexer, const char *fmt, ...) ATTR_FORMAT(2, 3); -inline static void sieve_lexer_warning - (struct sieve_lexer *lexer, const char *fmt, ...) ATTR_FORMAT(2, 3); - -inline static void sieve_lexer_error -(struct sieve_lexer *lexer, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - T_BEGIN { - sieve_verror(lexer->ehandler, - t_strdup_printf("%s:%d", sieve_script_name(lexer->script), - lexer->current_line), - fmt, args); - } T_END; - - va_end(args); -} - -inline static void sieve_lexer_warning -(struct sieve_lexer *lexer, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - T_BEGIN { - sieve_vwarning(lexer->ehandler, - t_strdup_printf("%s:%d", sieve_script_name(lexer->script), - lexer->current_line), - fmt, args); - } T_END; - - va_end(args); -} - struct sieve_lexer *sieve_lexer_create (struct sieve_script *script, struct sieve_error_handler *ehandler) { @@ -127,31 +111,40 @@ void sieve_lexer_free(struct sieve_lexer **lexer) *lexer = NULL; } -static void sieve_lexer_shift(struct sieve_lexer *lexer) +/* + * Internal error handling + */ + +inline static void sieve_lexer_error +(struct sieve_lexer *lexer, const char *fmt, ...) { - if ( lexer->buffer != NULL && lexer->buffer[lexer->buffer_pos] == '\n' ) - lexer->current_line++; - - if ( lexer->buffer != NULL && lexer->buffer_pos + 1 < lexer->buffer_size ) - lexer->buffer_pos++; - else { - if ( lexer->buffer != NULL ) - i_stream_skip(lexer->input, lexer->buffer_size); + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_verror(lexer->ehandler, + t_strdup_printf("%s:%d", sieve_script_name(lexer->script), + lexer->current_line), + fmt, args); + } T_END; - lexer->buffer = i_stream_get_data(lexer->input, &lexer->buffer_size); - - if ( lexer->buffer == NULL && i_stream_read(lexer->input) > 0 ) - lexer->buffer = i_stream_get_data(lexer->input, &lexer->buffer_size); - - lexer->buffer_pos = 0; - } + va_end(args); } -static inline int sieve_lexer_curchar(struct sieve_lexer *lexer) { - if ( lexer->buffer == NULL ) - return -1; - - return lexer->buffer[lexer->buffer_pos]; +inline static void sieve_lexer_warning +(struct sieve_lexer *lexer, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_vwarning(lexer->ehandler, + t_strdup_printf("%s:%d", sieve_script_name(lexer->script), + lexer->current_line), + fmt, args); + } T_END; + + va_end(args); } const char *sieve_lexer_token_string(struct sieve_lexer *lexer) @@ -185,6 +178,10 @@ const char *sieve_lexer_token_string(struct sieve_lexer *lexer) return "unknown token (bug)"; } +/* + * Debug + */ + void sieve_lexer_print_token(struct sieve_lexer *lexer) { switch ( lexer->token_type ) { @@ -217,17 +214,24 @@ void sieve_lexer_print_token(struct sieve_lexer *lexer) } } -enum sieve_token_type sieve_lexer_current_token(struct sieve_lexer *lexer) { +/* + * Token access + */ + +enum sieve_token_type sieve_lexer_current_token(struct sieve_lexer *lexer) +{ return lexer->token_type; } -const string_t *sieve_lexer_token_str(struct sieve_lexer *lexer) { +const string_t *sieve_lexer_token_str(struct sieve_lexer *lexer) +{ i_assert( lexer->token_type == STT_STRING ); return lexer->token_str_value; } -const char *sieve_lexer_token_ident(struct sieve_lexer *lexer) { +const char *sieve_lexer_token_ident(struct sieve_lexer *lexer) +{ i_assert( lexer->token_type == STT_TAG || lexer->token_type == STT_IDENTIFIER); @@ -235,24 +239,59 @@ const char *sieve_lexer_token_ident(struct sieve_lexer *lexer) { return str_c(lexer->token_str_value); } -int sieve_lexer_token_int(struct sieve_lexer *lexer) { +int sieve_lexer_token_int(struct sieve_lexer *lexer) +{ i_assert(lexer->token_type == STT_NUMBER); return lexer->token_int_value; } -bool sieve_lexer_eof(struct sieve_lexer *lexer) { +bool sieve_lexer_eof(struct sieve_lexer *lexer) +{ return lexer->token_type == STT_EOF; } -int sieve_lexer_current_line(struct sieve_lexer *lexer) { +int sieve_lexer_current_line(struct sieve_lexer *lexer) +{ return lexer->current_line; } +/* + * Lexical scanning + */ + +static void sieve_lexer_shift(struct sieve_lexer *lexer) +{ + if ( lexer->buffer != NULL && lexer->buffer[lexer->buffer_pos] == '\n' ) + lexer->current_line++; + + if ( lexer->buffer != NULL && lexer->buffer_pos + 1 < lexer->buffer_size ) + lexer->buffer_pos++; + else { + if ( lexer->buffer != NULL ) + i_stream_skip(lexer->input, lexer->buffer_size); + + lexer->buffer = i_stream_get_data(lexer->input, &lexer->buffer_size); + + if ( lexer->buffer == NULL && i_stream_read(lexer->input) > 0 ) + lexer->buffer = i_stream_get_data(lexer->input, &lexer->buffer_size); + + lexer->buffer_pos = 0; + } +} + +static inline int sieve_lexer_curchar(struct sieve_lexer *lexer) +{ + if ( lexer->buffer == NULL ) + return -1; + + return lexer->buffer[lexer->buffer_pos]; +} + /* sieve_lexer_scan_raw_token: * Scans valid tokens and whitespace */ -bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) +static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) { int start_line; string_t *str; @@ -511,9 +550,11 @@ bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) sieve_lexer_shift(lexer); } else { if ( sieve_lexer_curchar(lexer) == -1 ) { - sieve_lexer_error(lexer, "end of file before end of multi-line string"); + sieve_lexer_error(lexer, + "end of file before end of multi-line string"); } else { - sieve_lexer_error(lexer, "invalid character '%c' after 'text:' in multiline string", + sieve_lexer_error(lexer, + "invalid character '%c' after 'text:' in multiline string", sieve_lexer_curchar(lexer)); } @@ -546,7 +587,8 @@ bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) /* Scan the rest of the line */ while ( sieve_lexer_curchar(lexer) != '\n' ) { if ( sieve_lexer_curchar(lexer) == -1 ) { - sieve_lexer_error(lexer, "end of file before end of multi-line string"); + sieve_lexer_error(lexer, + "end of file before end of multi-line string"); lexer->token_type = STT_ERROR; return FALSE; } @@ -569,7 +611,8 @@ bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) /* Error (unknown character and EOF handled already) */ if ( lexer->token_type != STT_GARBAGE ) - sieve_lexer_error( lexer, "unexpected character(s) starting with '%c'", sieve_lexer_curchar(lexer) ); + sieve_lexer_error(lexer, "unexpected character(s) starting with '%c'", + sieve_lexer_curchar(lexer)); sieve_lexer_shift(lexer); lexer->token_type = STT_GARBAGE; return FALSE; diff --git a/src/lib-sieve/sieve-lexer.h b/src/lib-sieve/sieve-lexer.h index 4d8f6c3bd0ff8abe3189b2f7f00e59daa5094a4e..7d13b41e0afe4c826a5b994c1b4555c36768b267 100644 --- a/src/lib-sieve/sieve-lexer.h +++ b/src/lib-sieve/sieve-lexer.h @@ -1,8 +1,9 @@ +/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file + */ + #ifndef __SIEVE_LEXER_H #define __SIEVE_LEXER_H -#include "lib.h" - #include "sieve-common.h" enum sieve_token_type { @@ -34,21 +35,23 @@ enum sieve_token_type { /* Error tokens */ STT_GARBAGE, /* Error reporting deferred to parser */ - STT_ERROR /* Lexer is responsible for error, parser won't report additional errors */ + STT_ERROR /* Lexer is responsible for error, parser won't report additional + errors */ }; -struct sieve_token; struct sieve_lexer; +/* Lexer object */ struct sieve_lexer *sieve_lexer_create (struct sieve_script *script, struct sieve_error_handler *ehandler); void sieve_lexer_free(struct sieve_lexer **lexer); -bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer); +/* Scanning */ bool sieve_lexer_skip_token(struct sieve_lexer *lexer); const char *sieve_lexer_token_string(struct sieve_lexer *lexer); void sieve_lexer_print_token(struct sieve_lexer *lexer); +/* Token access */ enum sieve_token_type sieve_lexer_current_token(struct sieve_lexer *lexer); const string_t *sieve_lexer_token_str(struct sieve_lexer *lexer); const char *sieve_lexer_token_ident(struct sieve_lexer *lexer); diff --git a/src/lib-sieve/sieve-parser.c b/src/lib-sieve/sieve-parser.c index 0e2f8274a8d42c7599597174a5b2dca005abe5cb..90867af4908bb387e2d62518a689e53b2a15eb29 100644 --- a/src/lib-sieve/sieve-parser.c +++ b/src/lib-sieve/sieve-parser.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file + */ + #include <stdio.h> #include "lib.h" @@ -14,6 +17,22 @@ * nesting levels, etc. */ +/* + * Forward declarations + */ + +inline static void sieve_parser_error + (struct sieve_parser *parser, const char *fmt, ...) ATTR_FORMAT(2, 3); +inline static void sieve_parser_warning + (struct sieve_parser *parser, const char *fmt, ...) ATTR_FORMAT(2, 3); + +static bool sieve_parser_recover + (struct sieve_parser *parser, enum sieve_token_type end_token); + +/* + * Parser object + */ + struct sieve_parser { pool_t pool; @@ -27,55 +46,6 @@ struct sieve_parser { struct sieve_ast *ast; }; -inline static void sieve_parser_error - (struct sieve_parser *parser, const char *fmt, ...) ATTR_FORMAT(2, 3); -inline static void sieve_parser_warning - (struct sieve_parser *parser, const char *fmt, ...) ATTR_FORMAT(2, 3); - -inline static void sieve_parser_error - (struct sieve_parser *parser, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - /* Don't report a parse error if the lexer complained already */ - if ( sieve_lexer_current_token(parser->lexer) != STT_ERROR ) - { - T_BEGIN { - sieve_verror(parser->ehandler, - t_strdup_printf("%s:%d", - sieve_script_name(parser->script), - sieve_lexer_current_line(parser->lexer)), - fmt, args); - } T_END; - } - - parser->valid = FALSE; - - va_end(args); -} - -inline static void sieve_parser_warning - (struct sieve_parser *parser, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - T_BEGIN { - sieve_vwarning(parser->ehandler, - t_strdup_printf("%s:%d", - sieve_script_name(parser->script), - sieve_lexer_current_line(parser->lexer)), - fmt, args); - } T_END; - - va_end(args); -} - -/* Forward declarations */ -static bool sieve_parser_recover - (struct sieve_parser *parser, enum sieve_token_type end_token); - struct sieve_parser *sieve_parser_create (struct sieve_script *script, struct sieve_error_handler *ehandler) { @@ -121,20 +91,73 @@ void sieve_parser_free(struct sieve_parser **parser) *parser = NULL; } -/* arguments = *argument [test / test-list] - * argument = string-list / number / tag - * string = quoted-string / multi-line [[implicitly handled in lexer]] - * string-list = "[" string *("," string) "]" / string ;; if - * there is only a single string, the brackets are optional - * test-list = "(" test *("," test) ")" - * test = identifier arguments +/* + * Internal error handling */ -static bool sieve_parse_arguments - (struct sieve_parser *parser, struct sieve_ast_node *node) { + +inline static void sieve_parser_error +(struct sieve_parser *parser, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + /* Don't report a parse error if the lexer complained already */ + if ( sieve_lexer_current_token(parser->lexer) != STT_ERROR ) + { + T_BEGIN { + sieve_verror(parser->ehandler, + t_strdup_printf("%s:%d", + sieve_script_name(parser->script), + sieve_lexer_current_line(parser->lexer)), + fmt, args); + } T_END; + } + + parser->valid = FALSE; + va_end(args); +} + +inline static void sieve_parser_warning +(struct sieve_parser *parser, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_vwarning(parser->ehandler, + t_strdup_printf("%s:%d", + sieve_script_name(parser->script), + sieve_lexer_current_line(parser->lexer)), + fmt, args); + } T_END; + + va_end(args); +} + +/* + * Sieve grammar parsing + */ + +/* sieve_parse_arguments(): + * + * Parses both command arguments and sub-tests: + * arguments = *argument [test / test-list] + * argument = string-list / number / tag + * string = quoted-string / multi-line [[implicitly handled in lexer]] + * string-list = "[" string *("," string) "]" / string ;; if + * there is only a single string, the brackets are optional + * test-list = "(" test *("," test) ")" + * test = identifier arguments + */ +static bool 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, result = TRUE; + bool argument = TRUE; + bool result = TRUE; /* Indicates whether the parser is in a defined, not + necessarily error-free state */ /* Parse arguments */ while ( argument && result && @@ -145,25 +168,35 @@ static bool sieve_parse_arguments /* String list */ case STT_LSQUARE: + /* Create stinglist object */ arg = sieve_ast_argument_stringlist_create (node, sieve_lexer_current_line(parser->lexer)); - sieve_lexer_skip_token(lexer); + + sieve_lexer_skip_token(lexer); if ( sieve_lexer_current_token(lexer) == STT_STRING ) { + /* Add the string to the list */ sieve_ast_stringlist_add - (arg, sieve_lexer_token_str(lexer), sieve_lexer_current_line(parser->lexer)); + (arg, sieve_lexer_token_str(lexer), + sieve_lexer_current_line(parser->lexer)); + sieve_lexer_skip_token(lexer); while ( 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 - (arg, sieve_lexer_token_str(lexer), sieve_lexer_current_line(parser->lexer)); + (arg, sieve_lexer_token_str(lexer), + sieve_lexer_current_line(parser->lexer)); + sieve_lexer_skip_token(lexer); } else { - sieve_parser_error(parser, "expecting string after ',' in string list, but found %s", + sieve_parser_error(parser, + "expecting string after ',' in string list, but found %s", sieve_lexer_token_string(lexer)); result = sieve_parser_recover(parser, STT_RSQUARE); @@ -171,16 +204,19 @@ static bool sieve_parse_arguments } } } else { - sieve_parser_error(parser, "expecting string after '[' in string list, but found %s", + sieve_parser_error(parser, + "expecting string after '[' in string list, but found %s", sieve_lexer_token_string(lexer)); result = sieve_parser_recover(parser, STT_RSQUARE); } + /* Finish the string list */ if ( sieve_lexer_current_token(lexer) == STT_RSQUARE ) { sieve_lexer_skip_token(lexer); } else { - sieve_parser_error(parser, "expecting ',' or end of string list ']', but found %s", + sieve_parser_error(parser, + "expecting ',' or end of string list ']', but found %s", sieve_lexer_token_string(lexer)); if ( (result=sieve_parser_recover(parser, STT_RSQUARE)) == TRUE ) @@ -192,21 +228,24 @@ static bool sieve_parse_arguments /* Single string */ case STT_STRING: (void) sieve_ast_argument_string_create - (node, sieve_lexer_token_str(lexer), sieve_lexer_current_line(parser->lexer)); + (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 - (node, sieve_lexer_token_int(lexer), sieve_lexer_current_line(parser->lexer)); + (node, sieve_lexer_token_int(lexer), + sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); break; /* Tag */ case STT_TAG: (void) sieve_ast_argument_tag_create - (node, sieve_lexer_token_ident(lexer), sieve_lexer_current_line(parser->lexer)); + (node, sieve_lexer_token_ident(lexer), + sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); break; @@ -228,7 +267,8 @@ static bool sieve_parse_arguments /* Single test */ case STT_IDENTIFIER: test = sieve_ast_test_create - (node, sieve_lexer_token_ident(lexer), sieve_lexer_current_line(parser->lexer)); + (node, sieve_lexer_token_ident(lexer), + sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); /* Parse test arguments, which may include more tests (recurse) */ @@ -247,7 +287,8 @@ static bool sieve_parse_arguments /* Test starts with identifier */ if ( sieve_lexer_current_token(lexer) == STT_IDENTIFIER ) { test = sieve_ast_test_create - (node, sieve_lexer_token_ident(lexer), sieve_lexer_current_line(parser->lexer)); + (node, sieve_lexer_token_ident(lexer), + sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); /* Parse test arguments, which may include more tests (recurse) */ @@ -261,7 +302,8 @@ static bool sieve_parse_arguments /* Test starts with identifier */ if ( sieve_lexer_current_token(lexer) == STT_IDENTIFIER ) { test = sieve_ast_test_create - (node, sieve_lexer_token_ident(lexer), sieve_lexer_current_line(parser->lexer)); + (node, sieve_lexer_token_ident(lexer), + sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); /* Parse test arguments, which may include more tests (recurse) */ @@ -270,7 +312,8 @@ static bool sieve_parse_arguments break; } } else { - sieve_parser_error(parser, "expecting test identifier after ',' in test list, but found %s", + sieve_parser_error(parser, + "expecting test identifier after ',' in test list, but found %s", sieve_lexer_token_string(lexer)); result = sieve_parser_recover(parser, STT_RBRACKET); @@ -280,24 +323,26 @@ static bool sieve_parse_arguments } else result = sieve_parser_recover(parser, STT_RBRACKET); } else { - sieve_parser_error(parser, "expecting test identifier after '(' in test list, but found %s", + sieve_parser_error(parser, + "expecting test identifier after '(' in test list, but found %s", sieve_lexer_token_string(lexer)); result = sieve_parser_recover(parser, STT_RBRACKET); } /* The next token should be a ')', indicating the end of the test list - * --> privious sieve_parser_recover calls try to restore this situation after - * parse errors. + * --> previous sieve_parser_recover calls try to restore this situation + * after parse errors. */ if ( sieve_lexer_current_token(lexer) == STT_RBRACKET ) { sieve_lexer_skip_token(lexer); } else { - sieve_parser_error(parser, "expecting ',' or end of test list ')', but found %s", + sieve_parser_error(parser, + "expecting ',' or end of test list ')', but found %s", sieve_lexer_token_string(lexer)); - /* Recover function tries to make next token equal to ')'. If it succeeds we need to - * skip it. + /* 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 ) sieve_lexer_skip_token(lexer); @@ -319,8 +364,8 @@ static bool sieve_parse_arguments * block = "{" commands "}" */ static bool sieve_parse_commands - (struct sieve_parser *parser, struct sieve_ast_node *block) { - +(struct sieve_parser *parser, struct sieve_ast_node *block) +{ struct sieve_lexer *lexer = parser->lexer; bool result = TRUE; @@ -328,7 +373,8 @@ static bool sieve_parse_commands (parser->valid || sieve_errors_more_allowed(parser->ehandler)) ) { struct sieve_ast_node *command = sieve_ast_command_create - (block, sieve_lexer_token_ident(lexer), sieve_lexer_current_line(parser->lexer)); + (block, sieve_lexer_token_ident(lexer), + sieve_lexer_current_line(parser->lexer)); /* Defined state */ result = TRUE; @@ -337,13 +383,17 @@ static bool sieve_parse_commands result = sieve_parse_arguments(parser, command); - /* Check whether the command is properly terminated (i.e. with ; or a new block) */ + /* Check whether the command is properly terminated + * (i.e. with ; or a new block) + */ if ( result && sieve_lexer_current_token(lexer) != STT_SEMICOLON && sieve_lexer_current_token(lexer) != STT_LCURLY ) { - sieve_parser_error(parser, "expected end of command ';' or the beginning of a compound block '{', but found %s", - sieve_lexer_token_string(lexer)); + sieve_parser_error(parser, + "expected end of command ';' or the beginning of a compound block '{', " + "but found %s", + sieve_lexer_token_string(lexer)); result = FALSE; } @@ -370,7 +420,8 @@ static bool sieve_parse_commands if ( sieve_parse_commands(parser, command) ) { if ( sieve_lexer_current_token(lexer) != STT_RCURLY ) { - sieve_parser_error(parser, "expected end of compound block '}', but found %s", + sieve_parser_error(parser, + "expected end of compound block '}', but found %s", sieve_lexer_token_string(lexer)); result = sieve_parser_recover(parser, STT_RCURLY); } else @@ -390,11 +441,12 @@ static bool sieve_parse_commands } bool sieve_parser_run - (struct sieve_parser *parser, struct sieve_ast **ast) +(struct sieve_parser *parser, struct sieve_ast **ast) { if ( parser->ast != NULL ) sieve_ast_unref(&parser->ast); + /* Create AST object if none is provided */ if ( *ast == NULL ) *ast = sieve_ast_create(parser->script); else @@ -405,30 +457,33 @@ bool sieve_parser_run /* Scan first token */ sieve_lexer_skip_token(parser->lexer); - if ( sieve_parse_commands(parser, sieve_ast_root(parser->ast)) ) { + /* Parse */ + if ( sieve_parse_commands(parser, sieve_ast_root(parser->ast)) && + parser->valid ) { + + /* Parsed right to EOF ? */ if ( sieve_lexer_current_token(parser->lexer) != STT_EOF ) { sieve_parser_error(parser, "unexpected %s found at (the presumed) end of file", sieve_lexer_token_string(parser->lexer)); - - parser->ast = NULL; - sieve_ast_unref(ast); - return FALSE; + parser->valid = FALSE; } + } else parser->valid = FALSE; - return parser->valid; - } + /* Clean up AST if parse failed */ + if ( !parser->valid ) { + parser->ast = NULL; + sieve_ast_unref(ast); + } - parser->ast = NULL; - sieve_ast_unref(ast); - return FALSE; + return parser->valid; } /* Error recovery: - * To continue parsing after an error it is important to find the next parsible item in the - * stream. The recover function skips over the remaining garbage after an error. It tries - * to find the end of the failed syntax structure and takes nesting of structures into account. - * + * To continue parsing after an error it is important to find the next + * parsible item in the stream. The recover function skips over the remaining + * garbage after an error. It tries to find the end of the failed syntax + * structure and takes nesting of structures into account. */ /* Assign useful names to priorities for readability */ @@ -441,7 +496,9 @@ enum sieve_grammatical_prio { SGP_OTHER = -1 }; -static inline enum sieve_grammatical_prio __get_token_priority(enum sieve_token_type token) { +static inline enum sieve_grammatical_prio __get_token_priority +(enum sieve_token_type token) +{ switch ( token ) { case STT_LCURLY: case STT_RCURLY: @@ -461,7 +518,8 @@ static inline enum sieve_grammatical_prio __get_token_priority(enum sieve_token_ return SGP_OTHER; } -static bool sieve_parser_recover(struct sieve_parser *parser, enum sieve_token_type end_token) +static bool 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 * of ascending grammatical priority. @@ -499,7 +557,8 @@ static bool sieve_parser_recover(struct sieve_parser *parser, enum sieve_token_t } /* Special case: COMMAND */ - if (end_token == STT_SEMICOLON && sieve_lexer_current_token(lexer) == STT_LCURLY) + if (end_token == STT_SEMICOLON && + sieve_lexer_current_token(lexer) == STT_LCURLY) return TRUE; /* End not found before eof or end of surrounding grammatical structure diff --git a/src/lib-sieve/sieve-parser.h b/src/lib-sieve/sieve-parser.h index d12eb2a4d8e11222ea70f8918ed800f22d8375b7..e535ace3e981b060ff2a32d1708d6d7cdcb2bb47 100644 --- a/src/lib-sieve/sieve-parser.h +++ b/src/lib-sieve/sieve-parser.h @@ -1,3 +1,6 @@ +/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file + */ + #ifndef __SIEVE_PARSER_H #define __SIEVE_PARSER_H diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c index 890d28b22825da3eb92f70cb8bd189ff6fc75158..7e330420619594282a2045d4337ba09c7db24b8d 100644 --- a/src/lib-sieve/sieve.c +++ b/src/lib-sieve/sieve.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2002-2007 Dovecot Sieve authors, see the included COPYING file */ +/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file + */ #include "lib.h" #include "str.h" @@ -28,6 +29,10 @@ #include <unistd.h> #include <stdio.h> +/* + * Main Sieve library interface + */ + bool sieve_init(const char *plugins) { return sieve_extensions_init(plugins); @@ -38,6 +43,15 @@ void sieve_deinit(void) sieve_extensions_deinit(); } +const char *sieve_get_capabilities(void) +{ + return sieve_extensions_get_string(); +} + +/* + * Low-level compiler functions + */ + struct sieve_ast *sieve_parse (struct sieve_script *script, struct sieve_error_handler *ehandler) { @@ -83,6 +97,10 @@ static struct sieve_binary *sieve_generate return sbin; } +/* + * Sieve compilation + */ + struct sieve_binary *sieve_compile_script (struct sieve_script *script, struct sieve_error_handler *ehandler) { @@ -133,6 +151,10 @@ struct sieve_binary *sieve_compile return sbin; } +/* + * Reading/writing sieve binaries + */ + struct sieve_binary *sieve_open (const char *script_path, struct sieve_error_handler *ehandler) { @@ -140,33 +162,47 @@ struct sieve_binary *sieve_open struct sieve_binary *sbin; const char *binpath; + /* First open the scriptfile itself */ script = sieve_script_create(script_path, NULL, ehandler, NULL); - if ( script == NULL ) + if ( script == NULL ) { + /* Failed */ return NULL; + } T_BEGIN { + /* Then try to open the matching binary */ binpath = sieve_script_binpath(script); sbin = sieve_binary_open(binpath, script); if (sbin != NULL) { + /* Ok, it exists; now let's see if it is up to date */ if ( !sieve_binary_up_to_date(sbin) ) { + /* Not up to date */ sieve_binary_unref(&sbin); sbin = NULL; } else if ( !sieve_binary_load(sbin) ) { + /* Failed to load */ sieve_binary_unref(&sbin); sbin = NULL; } } + /* If the binary does not exist, is not up-to-date or fails to load, we need + * to (re-)compile. + */ if ( sbin == NULL ) { sbin = sieve_compile_script(script, ehandler); - if ( sbin != NULL ) + /* Save the binary if compile was successful */ + if ( sbin != NULL ) (void) sieve_binary_save(sbin, binpath); } } T_END; + /* Drop script reference, if sbin != NULL it holds a reference of its own. + * Otherwise the script object is freed here. + */ sieve_script_unref(&script); return sbin; @@ -178,6 +214,15 @@ bool sieve_save return sieve_binary_save(sbin, path); } +void sieve_close(struct sieve_binary **sbin) +{ + sieve_binary_unref(sbin); +} + +/* + * Debugging + */ + void sieve_dump(struct sieve_binary *sbin, struct ostream *stream) { struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin); @@ -207,6 +252,10 @@ int sieve_test return ret; } +/* + * Script execution + */ + int sieve_execute (struct sieve_binary *sbin, const struct sieve_message_data *msgdata, const struct sieve_script_env *senv, struct sieve_error_handler *ehandler, @@ -222,13 +271,3 @@ int sieve_execute sieve_interpreter_free(&interp); return ret; } - -void sieve_close(struct sieve_binary **sbin) -{ - sieve_binary_unref(sbin); -} - -const char *sieve_get_capabilities(void) -{ - return sieve_extensions_get_string(); -} diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h index ba207196751e58bf83b0d38d11f6b77385e9dc8d..12cd7eefe63134a60d7081ff55e02af245dd03cb 100644 --- a/src/lib-sieve/sieve.h +++ b/src/lib-sieve/sieve.h @@ -1,4 +1,5 @@ -/* Copyright (c) 2002-2007 Dovecot authors, see the included COPYING file */ +/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file + */ #ifndef __SIEVE_H #define __SIEVE_H @@ -10,6 +11,7 @@ #define SIEVE_VERSION "0.0.1" #define SIEVE_IMPLEMENTATION "Dovecot Sieve " SIEVE_VERSION +/* Enable runtime trace functionality */ #define SIEVE_RUNTIME_TRACE struct sieve_script; @@ -50,9 +52,30 @@ struct sieve_script_env { const char *user, time_t time); }; +/* + * Main Sieve library interface + */ + +/* sieve_init(): + * Initializes the sieve engine. Must be called before any sieve functionality + * is used. + */ bool sieve_init(const char *plugins); + +/* sieve_deinit(): + * Frees all memory allocated by the sieve engine. + */ void sieve_deinit(void); +/* sieve_get_capabilities: + * + */ +const char *sieve_get_capabilities(void); + +/* + * Script compilation + */ + /* sieve_compile_script: */ struct sieve_binary *sieve_compile_script @@ -65,6 +88,10 @@ struct sieve_binary *sieve_compile_script struct sieve_binary *sieve_compile (const char *scriptpath, struct sieve_error_handler *ehandler); +/* + * Reading/writing Sieve binaries + */ + /* sieve_open: * * First tries to open the binary version of the specified script and @@ -76,18 +103,28 @@ struct sieve_binary *sieve_compile struct sieve_binary *sieve_open (const char *scriptpath, struct sieve_error_handler *ehandler); -/* sieve_dump: - * - * Dumps the byte code in human-readable form to the specified ostream. - */ -void sieve_dump(struct sieve_binary *sbin, struct ostream *stream); - /* sieve_save: * Saves the binary as the file indicated by the path parameter. */ bool sieve_save (struct sieve_binary *sbin, const char *path); +/* sieve_close: + * + * Closes a compiled/opened sieve binary. + */ +void sieve_close(struct sieve_binary **sbin); + +/* + * Debugging + */ + +/* sieve_dump: + * + * Dumps the byte code in human-readable form to the specified ostream. + */ +void sieve_dump(struct sieve_binary *sbin, struct ostream *stream); + /* sieve_test: * * Executes the bytecode, but only prints the result to the given stream. @@ -97,6 +134,10 @@ int sieve_test const struct sieve_script_env *senv, struct ostream *stream, struct sieve_error_handler *ehandler, struct ostream *trace_stream); +/* + * Script execution + */ + /* sieve_execute: * * Executes the binary, including the result. @@ -106,15 +147,4 @@ int sieve_execute const struct sieve_script_env *senv, struct sieve_error_handler *ehandler, struct ostream *trace_stream); -/* sieve_close: - * - * Closes a compiled/opened sieve binary. - */ -void sieve_close(struct sieve_binary **sbin); - -/* sieve_get_capabilities: - * - */ -const char *sieve_get_capabilities(void); - #endif diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c index f54d2bd60284fa96df008dda85effe976e49fbf7..f735ed82a284f8fa71a343c71d358bec34f318a7 100644 --- a/src/plugins/lda-sieve/lda-sieve-plugin.c +++ b/src/plugins/lda-sieve/lda-sieve-plugin.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2002-2007 Dovecot authors, see the included COPYING file */ +/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file + */ #include "lib.h" #include "home-expand.h" @@ -90,9 +91,12 @@ static int lda_sieve_run const char *scriptlog; int ret = 0; + /* Create error handler */ scriptlog = t_strconcat(script_path, ".log", NULL); ehandler = sieve_logfile_ehandler_create(scriptlog, LDA_SIEVE_MAX_ERRORS); + /* Open the script */ + if ( debug ) i_info("sieve: Opening script %s", script_path); @@ -117,6 +121,7 @@ static int lda_sieve_run msgdata.auth_user = username; (void)mail_get_first_header(mail, "Message-ID", &msgdata.id); + /* Compose script execution environment */ memset(&scriptenv, 0, sizeof(scriptenv)); scriptenv.inbox = mailbox; scriptenv.namespaces = namespaces; @@ -128,6 +133,8 @@ static int lda_sieve_run scriptenv.duplicate_mark = duplicate_mark; scriptenv.duplicate_check = duplicate_check; + /* Execute the script */ + if ( debug ) i_info("sieve: Executing (in-memory) script %s", script_path); @@ -136,6 +143,8 @@ static int lda_sieve_run if ( ret < 0 ) i_error("sieve: Failed to execute script %s", script_path); + /* Clean up */ + sieve_close(&sbin); sieve_error_handler_unref(&ehandler); return ret; @@ -148,6 +157,8 @@ static int lda_sieve_deliver_mail const char *script_path; int ret; + /* Find the script to execute */ + script_path = lda_sieve_get_path(); if (script_path == NULL) return 0; @@ -155,6 +166,8 @@ static int lda_sieve_deliver_mail if (getenv("DEBUG") != NULL) i_info("sieve: Using sieve path: %s", script_path); + /* Run the script */ + T_BEGIN { ret = lda_sieve_run(namespaces, mail, script_path, destaddr, getenv("USER"), mailbox); @@ -165,15 +178,19 @@ static int lda_sieve_deliver_mail void sieve_plugin_init(void) { + /* Initialize Sieve engine */ sieve_init(""); + /* Hook into the delivery process */ next_deliver_mail = deliver_mail; deliver_mail = lda_sieve_deliver_mail; } void sieve_plugin_deinit(void) { + /* Remove hook */ deliver_mail = next_deliver_mail; + /* Deinitialize Sieve engine */ sieve_deinit(); }