From b0868f2efa43beebbef32ea0ae9417f11ae68c2d Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Fri, 26 Oct 2007 22:13:41 +0200 Subject: [PATCH] First steps towards implementing code generation and interpretation for proper comperators. --- sieve/tests/comparator.sieve | 6 + src/lib-sieve/Makefile.am | 2 + src/lib-sieve/plugins/vacation/ext-vacation.c | 37 ++++- src/lib-sieve/sieve-ast.c | 81 ++++++++-- src/lib-sieve/sieve-ast.h | 21 ++- src/lib-sieve/sieve-code.c | 82 ++++++----- src/lib-sieve/sieve-code.h | 16 +- src/lib-sieve/sieve-commands.h | 4 +- src/lib-sieve/sieve-common.h | 2 +- src/lib-sieve/sieve-comparators.c | 139 ++++++++++++++++++ src/lib-sieve/sieve-comparators.h | 19 +++ src/lib-sieve/sieve-generator.c | 17 +++ src/lib-sieve/sieve-generator.h | 3 + src/lib-sieve/sieve-validator.c | 95 +++++++----- src/lib-sieve/sieve-validator.h | 15 +- src/lib-sieve/tst-size.c | 25 ++-- 16 files changed, 447 insertions(+), 117 deletions(-) create mode 100644 sieve/tests/comparator.sieve create mode 100644 src/lib-sieve/sieve-comparators.c create mode 100644 src/lib-sieve/sieve-comparators.h diff --git a/sieve/tests/comparator.sieve b/sieve/tests/comparator.sieve new file mode 100644 index 000000000..12de98223 --- /dev/null +++ b/sieve/tests/comparator.sieve @@ -0,0 +1,6 @@ +if header :is :comparator "i;ascii-casemap" "from" "STEPHAN@drunksnipers.com" { + discard; + stop; +} + +keep; diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index 2af9df205..9c08e2289 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -37,6 +37,7 @@ libsieve_a_SOURCES = \ sieve-interpreter.c \ sieve-result.c \ sieve-error.c \ + sieve-comparators.c \ sieve-commands.c \ sieve-code.c \ $(tsts) \ @@ -55,6 +56,7 @@ noinst_HEADERS = \ sieve-interpreter.h \ sieve-result.h sieve-error.h \ + sieve-comparators.h \ sieve-commands.h \ sieve-code.h \ sieve-extensions.h \ diff --git a/src/lib-sieve/plugins/vacation/ext-vacation.c b/src/lib-sieve/plugins/vacation/ext-vacation.c index 64f2da6cf..a24ffc338 100644 --- a/src/lib-sieve/plugins/vacation/ext-vacation.c +++ b/src/lib-sieve/plugins/vacation/ext-vacation.c @@ -40,7 +40,10 @@ static bool cmd_vacation_validate_days_tag "the :days tag for the vacation command requires one number argument, but %s was found", sieve_ast_argument_name(*arg) ); return FALSE; } - + + /* Skip argument */ + *arg = sieve_ast_argument_next(*arg); + /* FIXME: assign somewhere */ return TRUE; @@ -64,6 +67,9 @@ static bool cmd_vacation_validate_subject_tag return FALSE; } + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + /* FIXME: assign somewhere */ return TRUE; @@ -86,7 +92,10 @@ static bool cmd_vacation_validate_from_tag sieve_ast_argument_name(*arg) ); return FALSE; } - + + /* Skip argument */ + *arg = sieve_ast_argument_next(*arg); + /* FIXME: assign somewhere */ return TRUE; @@ -112,6 +121,9 @@ static bool cmd_vacation_validate_addresses_tag /* FIXME: assign somewhere */ + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + return TRUE; } @@ -120,6 +132,9 @@ static bool cmd_vacation_validate_mime_tag struct sieve_ast_argument **arg ATTR_UNUSED, struct sieve_command_context *cmd ATTR_UNUSED) { + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + /* FIXME: assign somewhere */ return TRUE; @@ -150,12 +165,18 @@ static bool cmd_vacation_validate_handle_tag /* Command registration */ -static const struct sieve_tag vacation_days_tag = { "days", cmd_vacation_validate_days_tag }; -static const struct sieve_tag vacation_subject_tag = { "subject", cmd_vacation_validate_subject_tag }; -static const struct sieve_tag vacation_from_tag = { "from", cmd_vacation_validate_from_tag }; -static const struct sieve_tag vacation_addresses_tag = { "addresses", cmd_vacation_validate_addresses_tag }; -static const struct sieve_tag vacation_mime_tag = { "mime", cmd_vacation_validate_mime_tag }; -static const struct sieve_tag vacation_handle_tag = { "handle", cmd_vacation_validate_handle_tag }; +static const struct sieve_argument vacation_days_tag = + { "days", cmd_vacation_validate_days_tag, NULL }; +static const struct sieve_argument vacation_subject_tag = + { "subject", cmd_vacation_validate_subject_tag, NULL }; +static const struct sieve_argument vacation_from_tag = + { "from", cmd_vacation_validate_from_tag, NULL }; +static const struct sieve_argument vacation_addresses_tag = + { "addresses", cmd_vacation_validate_addresses_tag, NULL }; +static const struct sieve_argument vacation_mime_tag = + { "mime", cmd_vacation_validate_mime_tag, NULL }; +static const struct sieve_argument vacation_handle_tag = + { "handle", cmd_vacation_validate_handle_tag, NULL }; static bool cmd_vacation_registered(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) { diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c index ab1e45e2e..3b45748df 100644 --- a/src/lib-sieve/sieve-ast.c +++ b/src/lib-sieve/sieve-ast.c @@ -28,6 +28,7 @@ list->tail = node; \ } \ list->len++; \ + node->list = list; \ } /* List of AST nodes */ @@ -44,12 +45,49 @@ static struct sieve_ast_arg_list *sieve_ast_arg_list_create( pool_t pool ) static void sieve_ast_arg_list_add( struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument ) __LIST_ADD(list, argument) +static struct sieve_ast_argument *sieve_ast_arg_list_delete + (const struct sieve_ast_argument *first, const unsigned int count) +{ + const struct sieve_ast_argument *last; + unsigned int left; + + i_assert(first->list != NULL); + + /* Find the last of the deleted arguments */ + left = count - 1; + last = first; + while ( left > 0 && last->next != NULL ) { + left--; + last = last->next; + } + + /* Perform substitution */ + + if ( first->list->head == first ) + first->list->head = last->next; + + if ( first->list->tail == last ) + first->list->tail = first->prev; + + if ( first->prev != NULL ) + first->prev->next = last->next; + + if ( last->next != NULL ) + last->next->prev = first->prev; + + first->list->len -= count - left; + + /* Deleted objects are freed along with AST pool */ + + return last->next; +} + /* AST Node */ static struct sieve_ast_node *sieve_ast_node_create (struct sieve_ast *ast, struct sieve_ast_node *parent, enum sieve_ast_type type, - unsigned int source_line) { - + unsigned int source_line) +{ struct sieve_ast_node *node = p_new(ast->pool, struct sieve_ast_node, 1); node->ast = ast; @@ -71,7 +109,8 @@ static struct sieve_ast_node *sieve_ast_node_create return node; } -static void sieve_ast_node_add_command(struct sieve_ast_node *node, struct sieve_ast_node *command) { +static void 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); @@ -79,7 +118,8 @@ static void sieve_ast_node_add_command(struct sieve_ast_node *node, struct sieve sieve_ast_list_add(node->commands, command); } -static void sieve_ast_node_add_test(struct sieve_ast_node *node, struct sieve_ast_node *test) { +static void 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); @@ -87,7 +127,8 @@ static void sieve_ast_node_add_test(struct sieve_ast_node *node, struct sieve_as sieve_ast_list_add(node->tests, test); } -static void sieve_ast_node_add_argument(struct sieve_ast_node *node, struct sieve_ast_argument *argument) { +static void 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); @@ -97,8 +138,8 @@ static void sieve_ast_node_add_argument(struct sieve_ast_node *node, struct siev /* Argument AST node */ static struct sieve_ast_argument *sieve_ast_argument_create - (struct sieve_ast *ast, unsigned int source_line) { - + (struct sieve_ast *ast, unsigned int source_line) +{ struct sieve_ast_argument *arg = p_new(ast->pool, struct sieve_ast_argument, 1); arg->ast = ast; @@ -107,13 +148,15 @@ static struct sieve_ast_argument *sieve_ast_argument_create arg->next = NULL; arg->source_line = source_line; + arg->context = NULL; + arg->argument = NULL; return arg; } struct sieve_ast_argument *sieve_ast_argument_string_create - (struct sieve_ast_node *node, const string_t *str, unsigned int source_line) { - + (struct sieve_ast_node *node, const string_t *str, unsigned int source_line) +{ struct sieve_ast_argument *argument = sieve_ast_argument_create (node->ast, source_line); @@ -129,7 +172,8 @@ struct sieve_ast_argument *sieve_ast_argument_string_create } struct sieve_ast_argument *sieve_ast_argument_stringlist_create - (struct sieve_ast_node *node, unsigned int source_line) { + (struct sieve_ast_node *node, unsigned int source_line) +{ struct sieve_ast_argument *argument = sieve_ast_argument_create(node->ast, source_line); @@ -142,7 +186,8 @@ struct sieve_ast_argument *sieve_ast_argument_stringlist_create } void sieve_ast_stringlist_add - (struct sieve_ast_argument *list, const string_t *str, unsigned int source_line) { + (struct sieve_ast_argument *list, const string_t *str, unsigned int source_line) +{ struct sieve_ast_argument *stritem; i_assert( list->type == SAAT_STRING_LIST ); @@ -162,8 +207,8 @@ void sieve_ast_stringlist_add } struct sieve_ast_argument *sieve_ast_argument_tag_create - (struct sieve_ast_node *node, const char *tag, unsigned int source_line) { - + (struct sieve_ast_node *node, const char *tag, unsigned int source_line) +{ struct sieve_ast_argument *argument = sieve_ast_argument_create(node->ast, source_line); @@ -176,7 +221,8 @@ struct sieve_ast_argument *sieve_ast_argument_tag_create } struct sieve_ast_argument *sieve_ast_argument_number_create - (struct sieve_ast_node *node, int number, unsigned int source_line) { + (struct sieve_ast_node *node, int number, unsigned int source_line) +{ struct sieve_ast_argument *argument = sieve_ast_argument_create(node->ast, source_line); @@ -189,6 +235,12 @@ struct sieve_ast_argument *sieve_ast_argument_number_create return argument; } +struct sieve_ast_argument *sieve_ast_arguments_delete + (struct sieve_ast_argument *first, unsigned int count) +{ + return sieve_ast_arg_list_delete(first, count); +} + const char *sieve_ast_argument_name(struct sieve_ast_argument *argument) { switch ( argument->type ) { @@ -197,6 +249,7 @@ const char *sieve_ast_argument_name(struct sieve_ast_argument *argument) { case SAAT_STRING: return "a string"; case SAAT_NUMBER: return "a number"; case SAAT_TAG: return "a tag"; + default: return "??ARGUMENT??"; } } diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h index bcbeae6a6..1288e8d44 100644 --- a/src/lib-sieve/sieve-ast.h +++ b/src/lib-sieve/sieve-ast.h @@ -49,10 +49,12 @@ struct sieve_ast_value; enum sieve_ast_argument_type { SAAT_NONE, + + /* Arguments generated by parser */ SAAT_STRING, SAAT_STRING_LIST, SAAT_TAG, - SAAT_NUMBER + SAAT_NUMBER, }; struct sieve_ast_argument { @@ -61,9 +63,13 @@ struct sieve_ast_argument { /* Back reference to the AST object */ struct sieve_ast *ast; + /* List related */ + struct sieve_ast_arg_list *list; struct sieve_ast_argument *next; struct sieve_ast_argument *prev; + /* Parser-assigned data */ + union { string_t *str; struct sieve_ast_arg_list *strlist; @@ -72,8 +78,13 @@ struct sieve_ast_argument { } _value; unsigned int source_line; + + /* Assigned during validation */ - /* Arbitrary data associated with this ast element */ + /* Argument associated with this ast element */ + const struct sieve_argument *argument; + + /* Context data associated with this ast element */ void *context; }; @@ -105,7 +116,8 @@ struct sieve_ast_node { /* Back reference to this node's parent */ struct sieve_ast_node *parent; - /* Linked list references to tree siblings */ + /* Linked list references */ + struct sieve_ast_list *list; struct sieve_ast_node *next; struct sieve_ast_node *prev; @@ -144,6 +156,9 @@ struct sieve_ast_argument *sieve_ast_argument_tag_create struct sieve_ast_argument *sieve_ast_argument_number_create (struct sieve_ast_node *node, int number, unsigned int source_line); +struct sieve_ast_argument *sieve_ast_arguments_delete + (struct sieve_ast_argument *first, unsigned int count); + const char *sieve_ast_argument_name(struct sieve_ast_argument *argument); void sieve_ast_stringlist_add diff --git a/src/lib-sieve/sieve-code.c b/src/lib-sieve/sieve-code.c index fafb6c1be..b3c6ecde1 100644 --- a/src/lib-sieve/sieve-code.c +++ b/src/lib-sieve/sieve-code.c @@ -3,26 +3,32 @@ #include "sieve-code.h" #include "sieve-interpreter.h" -static bool sieve_code_dump_jmp(struct sieve_interpreter *interpreter); -static bool sieve_code_dump_jmptrue(struct sieve_interpreter *interpreter); -static bool sieve_code_dump_jmpfalse(struct sieve_interpreter *interpreter); -static bool sieve_code_dump_stop(struct sieve_interpreter *interpreter); -static bool sieve_code_dump_keep(struct sieve_interpreter *interpreter); -static bool sieve_code_dump_discard(struct sieve_interpreter *interpreter); - -static bool sieve_code_execute_jmp(struct sieve_interpreter *interpreter); -static bool sieve_code_execute_jmptrue(struct sieve_interpreter *interpreter); -static bool sieve_code_execute_jmpfalse(struct sieve_interpreter *interpreter); -static bool sieve_code_execute_stop(struct sieve_interpreter *interpreter); -static bool sieve_code_execute_keep(struct sieve_interpreter *interpreter); -static bool sieve_code_execute_discard(struct sieve_interpreter *interpreter); - -const struct sieve_opcode sieve_opcode_jmp = { sieve_code_dump_jmp, sieve_code_execute_jmp }; -const struct sieve_opcode sieve_opcode_jmptrue = { sieve_code_dump_jmptrue, sieve_code_execute_jmptrue }; -const struct sieve_opcode sieve_opcode_jmpfalse = { sieve_code_dump_jmpfalse, sieve_code_execute_jmpfalse }; -const struct sieve_opcode sieve_opcode_stop = { sieve_code_dump_stop, sieve_code_execute_stop }; -const struct sieve_opcode sieve_opcode_keep = { sieve_code_dump_keep, sieve_code_execute_keep }; -const struct sieve_opcode sieve_opcode_discard = { sieve_code_dump_discard, sieve_code_execute_discard }; +/* Operands */ + + + +/* Opcodes */ + +static bool opc_jmp_dump(struct sieve_interpreter *interpreter); +static bool opc_jmptrue_dump(struct sieve_interpreter *interpreter); +static bool opc_jmpfalse_dump(struct sieve_interpreter *interpreter); +static bool opc_stop_dump(struct sieve_interpreter *interpreter); +static bool opc_keep_dump(struct sieve_interpreter *interpreter); +static bool opc_discard_dump(struct sieve_interpreter *interpreter); + +static bool opc_jmp_execute(struct sieve_interpreter *interpreter); +static bool opc_jmptrue_execute(struct sieve_interpreter *interpreter); +static bool opc_jmpfalse_execute(struct sieve_interpreter *interpreter); +static bool opc_stop_execute(struct sieve_interpreter *interpreter); +static bool opc_keep_execute(struct sieve_interpreter *interpreter); +static bool opc_discard_execute(struct sieve_interpreter *interpreter); + +const struct sieve_opcode jmp_opcode = { opc_jmp_dump, opc_jmp_execute }; +const struct sieve_opcode jmptrue_opcode = { opc_jmptrue_dump, opc_jmptrue_execute }; +const struct sieve_opcode jmpfalse_opcode = { opc_jmpfalse_dump, opc_jmpfalse_execute }; +const struct sieve_opcode stop_opcode = { opc_stop_dump, opc_stop_execute }; +const struct sieve_opcode keep_opcode = { opc_keep_dump, opc_keep_execute }; +const struct sieve_opcode discard_opcode = { opc_discard_dump, opc_discard_execute }; extern const struct sieve_opcode tst_address_opcode; extern const struct sieve_opcode tst_header_opcode; @@ -31,12 +37,12 @@ extern const struct sieve_opcode tst_size_over_opcode; extern const struct sieve_opcode tst_size_under_opcode; const struct sieve_opcode *sieve_opcodes[] = { - &sieve_opcode_jmp, - &sieve_opcode_jmptrue, - &sieve_opcode_jmpfalse, - &sieve_opcode_stop, - &sieve_opcode_keep, - &sieve_opcode_discard, + &jmp_opcode, + &jmptrue_opcode, + &jmpfalse_opcode, + &stop_opcode, + &keep_opcode, + &discard_opcode, &tst_address_opcode, &tst_header_opcode, @@ -50,7 +56,7 @@ const unsigned int sieve_opcode_count = /* Code dump for core commands */ -static bool sieve_code_dump_jmp(struct sieve_interpreter *interpreter) +static bool opc_jmp_dump(struct sieve_interpreter *interpreter) { unsigned int pc = sieve_interpreter_program_counter(interpreter); int offset; @@ -63,7 +69,7 @@ static bool sieve_code_dump_jmp(struct sieve_interpreter *interpreter) return TRUE; } -static bool sieve_code_dump_jmptrue(struct sieve_interpreter *interpreter) +static bool opc_jmptrue_dump(struct sieve_interpreter *interpreter) { unsigned int pc = sieve_interpreter_program_counter(interpreter); int offset; @@ -76,7 +82,7 @@ static bool sieve_code_dump_jmptrue(struct sieve_interpreter *interpreter) return TRUE; } -static bool sieve_code_dump_jmpfalse(struct sieve_interpreter *interpreter) +static bool opc_jmpfalse_dump(struct sieve_interpreter *interpreter) { unsigned int pc = sieve_interpreter_program_counter(interpreter); int offset; @@ -89,21 +95,21 @@ static bool sieve_code_dump_jmpfalse(struct sieve_interpreter *interpreter) return TRUE; } -static bool sieve_code_dump_stop(struct sieve_interpreter *interpreter ATTR_UNUSED) +static bool opc_stop_dump(struct sieve_interpreter *interpreter ATTR_UNUSED) { printf("STOP\n"); return TRUE; } -static bool sieve_code_dump_keep(struct sieve_interpreter *interpreter ATTR_UNUSED) +static bool opc_keep_dump(struct sieve_interpreter *interpreter ATTR_UNUSED) { printf("KEEP\n"); return TRUE; } -static bool sieve_code_dump_discard(struct sieve_interpreter *interpreter ATTR_UNUSED) +static bool opc_discard_dump(struct sieve_interpreter *interpreter ATTR_UNUSED) { printf("DISCARD\n"); @@ -112,7 +118,7 @@ static bool sieve_code_dump_discard(struct sieve_interpreter *interpreter ATTR_U /* Code execution for core commands */ -static bool sieve_code_execute_jmp(struct sieve_interpreter *interpreter) +static bool opc_jmp_execute(struct sieve_interpreter *interpreter) { printf("JMP\n"); if ( !sieve_interpreter_program_jump(interpreter, TRUE) ) @@ -121,7 +127,7 @@ static bool sieve_code_execute_jmp(struct sieve_interpreter *interpreter) return TRUE; } -static bool sieve_code_execute_jmptrue(struct sieve_interpreter *interpreter) +static bool opc_jmptrue_execute(struct sieve_interpreter *interpreter) { if ( !sieve_interpreter_program_jump(interpreter, sieve_interpreter_get_test_result(interpreter)) ) @@ -132,7 +138,7 @@ static bool sieve_code_execute_jmptrue(struct sieve_interpreter *interpreter) return TRUE; } -static bool sieve_code_execute_jmpfalse(struct sieve_interpreter *interpreter) +static bool opc_jmpfalse_execute(struct sieve_interpreter *interpreter) { if ( !sieve_interpreter_program_jump(interpreter, !sieve_interpreter_get_test_result(interpreter)) ) @@ -143,21 +149,21 @@ static bool sieve_code_execute_jmpfalse(struct sieve_interpreter *interpreter) return TRUE; } -static bool sieve_code_execute_stop(struct sieve_interpreter *interpreter ATTR_UNUSED) +static bool opc_stop_execute(struct sieve_interpreter *interpreter ATTR_UNUSED) { printf(">> STOP\n"); return FALSE; } -static bool sieve_code_execute_keep(struct sieve_interpreter *interpreter ATTR_UNUSED) +static bool opc_keep_execute(struct sieve_interpreter *interpreter ATTR_UNUSED) { printf(">> KEEP\n"); return TRUE; } -static bool sieve_code_execute_discard(struct sieve_interpreter *interpreter ATTR_UNUSED) +static bool opc_discard_execute(struct sieve_interpreter *interpreter ATTR_UNUSED) { printf(">> DISCARD\n"); diff --git a/src/lib-sieve/sieve-code.h b/src/lib-sieve/sieve-code.h index 3e34eb5fc..c3a2041c1 100644 --- a/src/lib-sieve/sieve-code.h +++ b/src/lib-sieve/sieve-code.h @@ -9,6 +9,7 @@ typedef size_t sieve_size_t; +/* Opcode: identifies what's to be done */ struct sieve_opcode { bool (*dump)(struct sieve_interpreter *interpreter); bool (*execute)(struct sieve_interpreter *interpreter); @@ -31,10 +32,19 @@ enum sieve_core_operation { extern const struct sieve_opcode *sieve_opcodes[]; extern const unsigned int sieve_opcode_count; +/* Operand: argument to and opcode */ +struct sieve_operand { + bool (*dump)(struct sieve_interpreter *interpreter); + bool (*execute)(struct sieve_interpreter *interpreter); +}; + enum sieve_core_operand { - SIEVE_OPERAND_NUMBER = 0x01, - SIEVE_OPERAND_STRING = 0x02, - SIEVE_OPERAND_STRING_LIST = 0x03 + SIEVE_OPERAND_NUMBER, + SIEVE_OPERAND_STRING, + SIEVE_OPERAND_STRING_LIST, + SIEVE_OPERAND_COMPARATOR, + SIEVE_OPERAND_MATCH_TYPE, + SIEVE_OPERAND_ADDR_PART }; #define SIEVE_OPCODE_CORE_MASK 0x1F diff --git a/src/lib-sieve/sieve-commands.h b/src/lib-sieve/sieve-commands.h index 98148da09..42a9efb55 100644 --- a/src/lib-sieve/sieve-commands.h +++ b/src/lib-sieve/sieve-commands.h @@ -13,11 +13,13 @@ struct sieve_command_context; /* Command */ -struct sieve_tag { +struct sieve_argument { const char *identifier; bool (*validate)(struct sieve_validator *validator, struct sieve_ast_argument **arg, struct sieve_command_context *context); + bool (*generate)(struct sieve_generator *generator, struct sieve_ast_argument **arg, + struct sieve_command_context *context); }; enum sieve_command_type { diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h index 4bdce1511..60d83b922 100644 --- a/src/lib-sieve/sieve-common.h +++ b/src/lib-sieve/sieve-common.h @@ -11,7 +11,7 @@ struct sieve_ast_node; struct sieve_ast_argument; /* sieve-commands.h */ -struct sieve_tag; +struct sieve_argument; struct sieve_command; struct sieve_command_context; struct sieve_command_registration; diff --git a/src/lib-sieve/sieve-comparators.c b/src/lib-sieve/sieve-comparators.c new file mode 100644 index 000000000..1a451bed4 --- /dev/null +++ b/src/lib-sieve/sieve-comparators.c @@ -0,0 +1,139 @@ +#include "lib.h" +#include "compat.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-comparators.h" + +#include <string.h> + +/* + * Predeclarations + */ + +static int cmp_i_octet_compare(const void *val1, size_t val1_size, const void *val2, size_t val2_size); +static int cmp_i_ascii_casemap_compare(const void *val1, size_t val1_size, const void *val2, size_t val2_size); + +/* + * Comparator tag + */ + +static bool tag_comparator_validate + (struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd); + +const struct sieve_argument comparator_tag = + { "comparator", tag_comparator_validate, NULL }; + +static bool tag_comparator_validate + (struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd) +{ + struct sieve_ast_argument *tag = *arg; + const struct sieve_comparator *cmp; + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + /* Check syntax: + * ":comparator" <comparator-name: string> + */ + if ( (*arg)->type != SAAT_STRING ) { + sieve_command_validate_error(validator, cmd, + ":comparator tag requires one string argument, but %s was found", sieve_ast_argument_name(*arg) ); + return FALSE; + } + + /* Get comparator from registry */ + cmp = sieve_validator_find_comparator(validator, sieve_ast_argument_strc(*arg)); + + if ( cmp == NULL ) { + sieve_command_validate_error(validator, cmd, + "unknown comparator '%s'", sieve_ast_argument_strc(*arg)); + + return FALSE; + } + + /* String argument not needed during code generation, so delete it */ + *arg = sieve_ast_arguments_delete(*arg, 1); + + /* Store comparator in context */ + tag->context = (void *) cmp; + + return TRUE; +} + +static bool tag_comparator_generate + (struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd) +{ + +} + +/* + * Core comparators + */ + +const struct sieve_comparator i_octet_comparator = { + "i;octet", + cmp_i_octet_compare +}; + +const struct sieve_comparator i_ascii_casemap_comparator = { + "i;ascii-casemap", + cmp_i_ascii_casemap_compare +}; + +const struct sieve_comparator *sieve_core_comparators[] = { + &i_octet_comparator, &i_ascii_casemap_comparator +}; + +const unsigned int sieve_core_comparators_count = + (sizeof(sieve_core_comparators) / sizeof(sieve_core_comparators[0])); + + +static int cmp_i_octet_compare(const void *val1, size_t val1_size, const void *val2, size_t val2_size) +{ + int result; + + if ( val1_size == val2_size ) { + return memcmp(val1, val2, val1_size); + } + + if ( val1_size > val2_size ) { + result = memcmp(val1, val2, val2_size); + + if ( result == 0 ) return 1; + + return result; + } + + result = memcmp(val1, val2, val1_size); + + if ( result == 0 ) return -1; + + return result; +} + +static int cmp_i_ascii_casemap_compare(const void *val1, size_t val1_size, const void *val2, size_t val2_size) +{ + int result; + + if ( val1_size == val2_size ) { + return strncasecmp((const char *) val1, (const char *) val2, val1_size); + } + + if ( val1_size > val2_size ) { + result = strncasecmp((const char *) val1, (const char *) val2, val2_size); + + if ( result == 0 ) return 1; + + return result; + } + + result = strncasecmp((const char *) val1, (const char *) val2, val1_size); + + if ( result == 0 ) return -1; + + return result; +} diff --git a/src/lib-sieve/sieve-comparators.h b/src/lib-sieve/sieve-comparators.h new file mode 100644 index 000000000..5583a02d2 --- /dev/null +++ b/src/lib-sieve/sieve-comparators.h @@ -0,0 +1,19 @@ +#ifndef __SIEVE_COMPARATORS_H +#define __SIEVE_COMPARATORS_H + +struct sieve_comparator { + const char *identifier; + + /* Equality, ordering, prefix and substring match */ + + /* ( output similar to strncmp ) */ + int (*compare)(const void *val1, size_t val1_size, const void *val2, size_t val2_size); +}; + +extern const struct sieve_argument comparator_tag; + +extern const struct sieve_comparator *sieve_core_comparators[]; +extern const unsigned int sieve_core_comparators_count; + + +#endif /* __SIEVE_COMPARATORS_H */ diff --git a/src/lib-sieve/sieve-generator.c b/src/lib-sieve/sieve-generator.c index e5a715817..7dfbd47aa 100644 --- a/src/lib-sieve/sieve-generator.c +++ b/src/lib-sieve/sieve-generator.c @@ -266,6 +266,23 @@ sieve_size_t sieve_generator_emit_ext_opcode /* Generator functions */ +bool sieve_generate_arguments(struct sieve_generator *generator, + struct sieve_command_context *cmd, struct sieve_ast_argument **arg) +{ + /* Parse all arguments with assigned generator function */ + while ( *arg != NULL && (*arg)->argument != NULL) { + const struct sieve_argument *argument = (*arg)->argument; + + /* Call the generation function for the argument */ + if ( argument->generate != NULL ) { + if ( !argument->generate(generator, arg, cmd) ) + return FALSE; + } else break; + } + + return TRUE; +} + bool sieve_generate_test (struct sieve_generator *generator, struct sieve_ast_node *tst_node, struct sieve_jumplist *jlist, bool jump_true) diff --git a/src/lib-sieve/sieve-generator.h b/src/lib-sieve/sieve-generator.h index cffaf0580..73a7f0a9e 100644 --- a/src/lib-sieve/sieve-generator.h +++ b/src/lib-sieve/sieve-generator.h @@ -54,6 +54,9 @@ bool sieve_generator_emit_stringlist_argument (struct sieve_generator *generator, struct sieve_ast_argument *arg); /* AST generation API */ +bool sieve_generate_arguments(struct sieve_generator *generator, + struct sieve_command_context *cmd, struct sieve_ast_argument **arg); + bool sieve_generate_block(struct sieve_generator *generator, struct sieve_ast_node *block); bool sieve_generate_test(struct sieve_generator *generator, struct sieve_ast_node *tst_node, struct sieve_jumplist *jlist, bool jump_true); diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c index 8da75647c..22b501d8c 100644 --- a/src/lib-sieve/sieve-validator.c +++ b/src/lib-sieve/sieve-validator.c @@ -7,6 +7,7 @@ #include "sieve-commands-private.h" #include "sieve-validator.h" #include "sieve-extensions.h" +#include "sieve-comparators.h" /* Context/Semantics checker implementation */ @@ -17,13 +18,15 @@ struct sieve_validator { struct sieve_error_handler *ehandler; /* Registries */ - struct hash_table *commands; + struct hash_table *commands; + struct hash_table *comparators; }; /* Predeclared statics */ static void sieve_validator_register_core_commands(struct sieve_validator *validator); static void sieve_validator_register_core_tests(struct sieve_validator *validator); +static void sieve_validator_register_core_comparators(struct sieve_validator *validator); /* Error management */ @@ -66,12 +69,18 @@ struct sieve_validator *sieve_validator_create(struct sieve_ast *ast, struct sie sieve_validator_register_core_commands(validator); sieve_validator_register_core_tests(validator); + /* Setup comparator registry */ + validator->comparators = hash_create + (pool, pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); + sieve_validator_register_core_comparators(validator); + return validator; } void sieve_validator_free(struct sieve_validator *validator) { hash_destroy(&validator->commands); + hash_destroy(&validator->comparators); sieve_ast_unref(&validator->ast); pool_unref(&(validator->pool)); @@ -165,11 +174,11 @@ static bool _unknown_tag_validate return FALSE; } -static const struct sieve_tag _unknown_tag = { "", _unknown_tag_validate }; +static const struct sieve_argument _unknown_tag = { "", _unknown_tag_validate, NULL }; void sieve_validator_register_tag (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, - const struct sieve_tag *tag) + const struct sieve_argument *tag) { if ( cmd_reg->tags == NULL ) { cmd_reg->tags = hash_create @@ -191,12 +200,12 @@ static void sieve_validator_register_unknown_tag hash_insert(cmd_reg->tags, (void *) tag, (void *) &_unknown_tag); } -static const struct sieve_tag *sieve_validator_find_tag +static const struct sieve_argument *sieve_validator_find_tag (struct sieve_command_registration *cmd_reg, const char *tag) { if ( cmd_reg->tags == NULL ) return NULL; - return (struct sieve_tag *) hash_lookup(cmd_reg->tags, tag); + return (struct sieve_argument *) hash_lookup(cmd_reg->tags, tag); } /* Extension support */ @@ -222,30 +231,30 @@ const struct sieve_extension *sieve_validator_load_extension return ext; } -/* Comparator validation */ +/* Comparator registry */ -static bool sieve_validate_comparator_tag - (struct sieve_validator *validator, struct sieve_ast_argument **arg, - struct sieve_command_context *cmd) +void sieve_validator_register_comparator + (struct sieve_validator *validator, const struct sieve_comparator *cmp) { - /* Only one possible tag, so we don't bother checking the identifier */ - *arg = sieve_ast_argument_next(*arg); + hash_insert(validator->comparators, (void *) cmp->identifier, (void *) cmp); +} + +const struct sieve_comparator *sieve_validator_find_comparator + (struct sieve_validator *validator, const char *comparator) +{ + return (const struct sieve_comparator *) hash_lookup(validator->comparators, comparator); +} + +static void sieve_validator_register_core_comparators(struct sieve_validator *validator) +{ + unsigned int i; - /* Check syntax: - * ":comparator" <comparator-name: string> - */ - if ( (*arg)->type != SAAT_STRING ) { - sieve_command_validate_error(validator, cmd, - ":comparator tag requires one string argument, but %s was found", sieve_ast_argument_name(*arg) ); - return FALSE; + for ( i = 0; i < sieve_core_comparators_count; i++ ) { + sieve_validator_register_comparator(validator, sieve_core_comparators[i]); } - - /* FIXME: assign comparator somewhere */ - - return TRUE; } -static const struct sieve_tag comparator_tag = { "comparator", sieve_validate_comparator_tag }; +/* Comparator validation */ void sieve_validator_link_comparator_tag (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) @@ -257,9 +266,13 @@ void sieve_validator_link_comparator_tag static bool sieve_validate_match_type_tag (struct sieve_validator *validator ATTR_UNUSED, - struct sieve_ast_argument **arg ATTR_UNUSED, + struct sieve_ast_argument **arg, struct sieve_command_context *cmd ATTR_UNUSED) { + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + /* Syntax: * ":is" / ":contains" / ":matches" */ @@ -269,9 +282,12 @@ static bool sieve_validate_match_type_tag return TRUE; } -static const struct sieve_tag match_is_tag = { "is", sieve_validate_match_type_tag }; -static const struct sieve_tag match_contains_tag = { "contains", sieve_validate_match_type_tag }; -static const struct sieve_tag match_matches_tag = { "matches", sieve_validate_match_type_tag }; +static const struct sieve_argument match_is_tag = + { "is", sieve_validate_match_type_tag, NULL }; +static const struct sieve_argument match_contains_tag = + { "contains", sieve_validate_match_type_tag, NULL }; +static const struct sieve_argument match_matches_tag = + { "matches", sieve_validate_match_type_tag, NULL }; void sieve_validator_link_match_type_tags (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) @@ -285,9 +301,12 @@ void sieve_validator_link_match_type_tags static bool sieve_validate_address_part_tag (struct sieve_validator *validator ATTR_UNUSED, - struct sieve_ast_argument **arg ATTR_UNUSED, + struct sieve_ast_argument **arg, struct sieve_command_context *cmd ATTR_UNUSED) { + /* Skip argument */ + *arg = sieve_ast_argument_next(*arg); + /* Syntax: * ":localpart" / ":domain" / ":all" */ @@ -297,9 +316,12 @@ static bool sieve_validate_address_part_tag return TRUE; } -static const struct sieve_tag address_localpart_tag = { "localpart", sieve_validate_address_part_tag }; -static const struct sieve_tag address_domain_tag = { "domain", sieve_validate_address_part_tag }; -static const struct sieve_tag address_all_tag = { "all", sieve_validate_address_part_tag }; +static const struct sieve_argument address_localpart_tag = + { "localpart", sieve_validate_address_part_tag, NULL }; +static const struct sieve_argument address_domain_tag = + { "domain", sieve_validate_address_part_tag, NULL }; +static const struct sieve_argument address_all_tag = + { "all", sieve_validate_address_part_tag, NULL }; void sieve_validator_link_address_part_tags (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) @@ -344,7 +366,7 @@ bool sieve_validate_command_arguments /* Parse tagged and optional arguments */ while ( sieve_ast_argument_type(arg) == SAAT_TAG ) { - const struct sieve_tag *tag = sieve_validator_find_tag(cmd_reg, sieve_ast_argument_tag(arg)); + const struct sieve_argument *tag = sieve_validator_find_tag(cmd_reg, sieve_ast_argument_tag(arg)); if ( tag == NULL ) { sieve_command_validate_error(validator, cmd, @@ -358,16 +380,17 @@ bool sieve_validate_command_arguments if ( *(tag->identifier) == '\0' ) return FALSE; + /* Assign the tagged argument type to the ast for later reference (in generator) */ + arg->argument = tag; + /* Call the validation function for the tag (if present) * Fail if the validation fails. */ if ( tag->validate != NULL ) { if ( !tag->validate(validator, &arg, cmd) ) return FALSE; - } - - /* Skip to next argument */ - arg = sieve_ast_argument_next(arg); + } else + arg = sieve_ast_argument_next(arg); } /* Remaining arguments should be positional (tags are not allowed here) */ diff --git a/src/lib-sieve/sieve-validator.h b/src/lib-sieve/sieve-validator.h index a1bd69f18..9ee059aee 100644 --- a/src/lib-sieve/sieve-validator.h +++ b/src/lib-sieve/sieve-validator.h @@ -3,6 +3,7 @@ #include "lib.h" +#include "sieve-comparators.h" #include "sieve-common.h" #include "sieve-error.h" @@ -25,12 +26,18 @@ void sieve_validator_error void sieve_validator_register_command (struct sieve_validator *validator, const struct sieve_command *command); -/* Tag registration */ +/* Argument registration */ void sieve_validator_register_tag (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, - const struct sieve_tag *tag); - -/* Special test tags */ + const struct sieve_argument *argument); + +/* Comparator registration */ +void sieve_validator_register_comparator + (struct sieve_validator *validator, const struct sieve_comparator *cmp); +const struct sieve_comparator *sieve_validator_find_comparator + (struct sieve_validator *validator, const char *comparator); + +/* Special test arguments */ void sieve_validator_link_comparator_tag (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg); void sieve_validator_link_match_type_tags diff --git a/src/lib-sieve/tst-size.c b/src/lib-sieve/tst-size.c index 20f89cd76..4a1bb8288 100644 --- a/src/lib-sieve/tst-size.c +++ b/src/lib-sieve/tst-size.c @@ -23,7 +23,6 @@ const struct sieve_opcode tst_size_under_opcode = struct tst_size_context_data { enum { SIZE_UNASSIGNED, SIZE_UNDER, SIZE_OVER } type; - unsigned int limit; }; #define TST_SIZE_ERROR_DUP_TAG \ @@ -33,7 +32,7 @@ struct tst_size_context_data { static bool tst_size_validate_over_tag (struct sieve_validator *validator, - struct sieve_ast_argument **arg ATTR_UNUSED, + struct sieve_ast_argument **arg, struct sieve_command_context *tst) { struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) tst->data; @@ -44,6 +43,10 @@ static bool tst_size_validate_over_tag } ctx_data->type = SIZE_OVER; + + /* Delete this tag */ + *arg = sieve_ast_arguments_delete(*arg, 1); + return TRUE; } @@ -59,14 +62,20 @@ static bool tst_size_validate_under_tag return FALSE; } - ctx_data->type = SIZE_UNDER; + ctx_data->type = SIZE_UNDER; + + /* Delete this tag */ + *arg = sieve_ast_arguments_delete(*arg, 1); + return TRUE; } /* Test registration */ -static const struct sieve_tag size_over_tag = { "over", tst_size_validate_over_tag }; -static const struct sieve_tag size_under_tag = { "under", tst_size_validate_under_tag }; +static const struct sieve_argument size_over_tag = + { "over", tst_size_validate_over_tag, NULL }; +static const struct sieve_argument size_under_tag = + { "under", tst_size_validate_under_tag, NULL }; bool tst_size_registered(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) { @@ -86,7 +95,6 @@ bool tst_size_validate(struct sieve_validator *validator, struct sieve_command_c /* Assign context */ ctx_data = p_new(sieve_command_pool(tst), struct tst_size_context_data, 1); ctx_data->type = SIZE_UNASSIGNED; - ctx_data->limit = 0; tst->data = ctx_data; /* Check envelope test syntax: @@ -109,8 +117,6 @@ bool tst_size_validate(struct sieve_validator *validator, struct sieve_command_c return FALSE; } - ctx_data->limit = sieve_ast_argument_number(arg); - return TRUE; } @@ -120,6 +126,7 @@ bool tst_size_generate (struct sieve_generator *generator, struct sieve_command_context *ctx) { + struct sieve_ast_argument *arg = sieve_ast_argument_first(ctx->ast_node); struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) ctx->data; if ( ctx_data->type == SIZE_OVER ) @@ -127,7 +134,7 @@ bool tst_size_generate else sieve_generator_emit_opcode(generator, SIEVE_OPCODE_SIZEUNDER); - sieve_generator_emit_number(generator, ctx_data->limit); + sieve_generator_emit_number(generator, sieve_ast_argument_number(arg)); return TRUE; } -- GitLab