diff --git a/Makefile.am b/Makefile.am index 7261c7867206235b19944457a3c30146741d79e2..18b7c0e3db895ed58f2657756273e8c89d538fd2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,6 +14,11 @@ endif # Testsuite tests +if BUILD_ENOTIFY +ENOTIFY_TESTS = \ + tests/extensions/enotify/basic.svtest +endif + TESTSUITE_BIN = $(top_srcdir)/src/testsuite/testsuite test_cases = \ @@ -57,7 +62,8 @@ test_cases = \ tests/extensions/subaddress/basic.svtest \ tests/extensions/subaddress/rfc.svtest \ tests/extensions/vacation/errors.svtest \ - tests/extensions/vacation/execute.svtest + tests/extensions/vacation/execute.svtest \ + $(ENOTIFY_TESTS) $(test_cases): @$(TESTSUITE_BIN) $@ diff --git a/src/lib-sieve/plugins/enotify/cmd-notify.c b/src/lib-sieve/plugins/enotify/cmd-notify.c index 7916f11eeeade478ab424ab6ab8f99c9051133f9..494775244e2dd81d74c28f21d637579ae0cfcbb0 100644 --- a/src/lib-sieve/plugins/enotify/cmd-notify.c +++ b/src/lib-sieve/plugins/enotify/cmd-notify.c @@ -67,16 +67,12 @@ static bool cmd_notify_validate_string_tag static bool cmd_notify_validate_stringlist_tag (struct sieve_validator *validator, struct sieve_ast_argument **arg, struct sieve_command_context *cmd); +static bool cmd_notify_validate_importance_tag + (struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd); /* Argument objects */ -static const struct sieve_argument notify_importance_tag = { - "importance", - NULL, NULL, - cmd_notify_validate_string_tag, - NULL, NULL -}; - static const struct sieve_argument notify_from_tag = { "from", NULL, NULL, @@ -98,33 +94,40 @@ static const struct sieve_argument notify_message_tag = { NULL, NULL }; +static const struct sieve_argument notify_importance_tag = { + "importance", + NULL, NULL, + cmd_notify_validate_importance_tag, + NULL, NULL +}; + /* Codes for optional arguments */ enum cmd_notify_optional { OPT_END, - OPT_IMPORTANCE, OPT_FROM, OPT_OPTIONS, - OPT_MESSAGE + OPT_MESSAGE, + OPT_IMPORTANCE }; /* * Notify operation */ -static bool ext_notify_operation_dump +static bool cmd_notify_operation_dump (const struct sieve_operation *op, const struct sieve_dumptime_env *denv, sieve_size_t *address); -static int ext_notify_operation_execute +static int cmd_notify_operation_execute (const struct sieve_operation *op, const struct sieve_runtime_env *renv, sieve_size_t *address); const struct sieve_operation notify_operation = { - "notify", + "NOTIFY", &enotify_extension, - 0, - ext_notify_operation_dump, - ext_notify_operation_execute + EXT_ENOTIFY_OPERATION_NOTIFY, + cmd_notify_operation_dump, + cmd_notify_operation_execute }; /* @@ -183,7 +186,6 @@ static bool cmd_notify_validate_string_tag /* Check syntax: * :from <string> - * :importance <"1" / "2" / "3"> * :message <string> */ if ( !sieve_validate_tag_parameter @@ -196,7 +198,7 @@ static bool cmd_notify_validate_string_tag *arg = sieve_ast_argument_next(*arg); } else if ( tag->argument == ¬ify_message_tag ) { - /* Detach optional argument (emitted as mandatory) */ + /* Skip parameter */ *arg = sieve_ast_argument_next(*arg); } @@ -226,6 +228,48 @@ static bool cmd_notify_validate_stringlist_tag return TRUE; } +static bool cmd_notify_validate_importance_tag +(struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd ATTR_UNUSED) +{ + const struct sieve_ast_argument *tag = *arg; + const char *impstr; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :importance <"1" / "2" / "3"> + */ + + if ( sieve_ast_argument_type(*arg) != SAAT_STRING ) { + /* Not a string */ + sieve_argument_validate_error(validator, *arg, + "the :importance tag for the notify command requires a string parameter, " + "but %s was found", sieve_ast_argument_name(*arg)); + return FALSE; + } + + impstr = sieve_ast_argument_strc(*arg); + + if ( impstr[0] < '1' || impstr[0] > '3' || impstr[1] != '\0' ) { + /* Invalid importance */ + sieve_argument_validate_error(validator, *arg, + "invalid :importance value for notify command: %s", impstr); + return FALSE; + } + + sieve_ast_argument_number_substitute(*arg, impstr[0] - '0'); + (*arg)->arg_id_code = tag->arg_id_code; + (*arg)->argument = &number_argument; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + + /* * Command registration */ @@ -282,7 +326,7 @@ static bool cmd_notify_generate * Code dump */ -static bool ext_notify_operation_dump +static bool cmd_notify_operation_dump (const struct sieve_operation *op ATTR_UNUSED, const struct sieve_dumptime_env *denv, sieve_size_t *address) { @@ -337,7 +381,7 @@ static bool ext_notify_operation_dump * Code execution */ -static int ext_notify_operation_execute +static int cmd_notify_operation_execute (const struct sieve_operation *op ATTR_UNUSED, const struct sieve_runtime_env *renv, sieve_size_t *address) { @@ -378,8 +422,10 @@ static int ext_notify_operation_execute } /* Enforce 0 < importance < 4 (just to be sure) */ - if ( importance < 1 || importance > 3 ) + if ( importance < 1 ) importance = 1; + else if ( importance > 3 ) + importance = 3; break; case OPT_FROM: if ( !sieve_opr_string_read(renv, address, &from) ) { @@ -387,6 +433,12 @@ static int ext_notify_operation_execute return SIEVE_EXEC_BIN_CORRUPT; } break; + case OPT_MESSAGE: + if ( !sieve_opr_string_read(renv, address, &message) ) { + sieve_runtime_trace_error(renv, "invalid from operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + break; case OPT_OPTIONS: if ( (options=sieve_opr_stringlist_read(renv, address)) == NULL ) { sieve_runtime_trace_error(renv, "invalid options operand"); @@ -394,7 +446,8 @@ static int ext_notify_operation_execute } break; default: - sieve_runtime_trace_error(renv, "unknown optional operand"); + sieve_runtime_trace_error(renv, "unknown optional operand: %d", + opt_code); return SIEVE_EXEC_BIN_CORRUPT; } } diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.h b/src/lib-sieve/plugins/enotify/ext-enotify-common.h index 664cf10d144cda7af19a79f42b2780aedd73bcd6..d097b4faf0886e94c085bd6e90504e5f11668252 100644 --- a/src/lib-sieve/plugins/enotify/ext-enotify-common.h +++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.h @@ -16,4 +16,14 @@ extern const struct sieve_extension enotify_extension; extern const struct sieve_command notify_command; +/* + * Operands + */ + +extern const struct sieve_operation notify_operation; + +enum ext_variables_opcode { + EXT_ENOTIFY_OPERATION_NOTIFY +}; + #endif /* __EXT_ENOTIFY_COMMON_H */ diff --git a/src/lib-sieve/plugins/enotify/ext-enotify.c b/src/lib-sieve/plugins/enotify/ext-enotify.c index 8b02dbe6d73f6627a1c74e86ce519cc4efdb4bcc..cdcabeb3c9011d6617bbea5ff8338510b67e65da 100644 --- a/src/lib-sieve/plugins/enotify/ext-enotify.c +++ b/src/lib-sieve/plugins/enotify/ext-enotify.c @@ -26,6 +26,14 @@ #include "ext-enotify-common.h" +/* + * Operations + */ + +const struct sieve_operation *ext_enotify_operations[] = { + ¬ify_operation +}; + /* * Extension */ @@ -41,7 +49,7 @@ const struct sieve_extension enotify_extension = { ext_enotify_load, ext_enotify_validator_load, NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, + SIEVE_EXT_DEFINE_OPERATION(notify_operation), SIEVE_EXT_DEFINE_NO_OPERANDS }; diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c index 5e57d31a02dbdd4d658dd9e58456f9665fb17499..7a0782a7dc92b414ce5071d1ff7daf64414c9bad 100644 --- a/src/lib-sieve/sieve-ast.c +++ b/src/lib-sieve/sieve-ast.c @@ -492,6 +492,13 @@ void sieve_ast_argument_string_setc str_append(argument->_value.str, newstr); } +void sieve_ast_argument_number_substitute +(struct sieve_ast_argument *argument, unsigned int number) +{ + argument->type = SAAT_NUMBER; + argument->_value.number = number; +} + struct sieve_ast_argument *sieve_ast_argument_stringlist_create (struct sieve_ast_node *node, unsigned int source_line) { diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h index 7e4b424cb1a44f5b429de82614616dc59e5b4376..f2fc4e3e27d8e6e6f0ac9e62447cb2b70756e026 100644 --- a/src/lib-sieve/sieve-ast.h +++ b/src/lib-sieve/sieve-ast.h @@ -246,7 +246,9 @@ void sieve_ast_argument_string_setc (struct sieve_ast_argument *argument, const char *newstr); void sieve_ast_argument_number_set - (struct sieve_ast_argument *argument, unsigned int newnum); + (struct sieve_ast_argument *argument, unsigned int newnum); +void sieve_ast_argument_number_substitute + (struct sieve_ast_argument *argument, unsigned int number); struct sieve_ast_argument *sieve_ast_argument_tag_insert (struct sieve_ast_argument *before, const char *tag, unsigned int source_line); diff --git a/tests/extensions/enotify/basic.svtest b/tests/extensions/enotify/basic.svtest new file mode 100644 index 0000000000000000000000000000000000000000..65397e24492257369c9cfafe9720832b829180dc --- /dev/null +++ b/tests/extensions/enotify/basic.svtest @@ -0,0 +1,10 @@ +require "vnd.dovecot.testsuite"; +require "enotify"; + +test "Valid notification" { + /* Test to catch runtime segfaults */ + notify + :message "This is probably very important" + :importance "1" + "mailto:alm@example.com"; +}