From f2a167f73b7421c8bf741e70196b56f52015f6c7 Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Tue, 20 Oct 2009 22:54:11 +0200 Subject: [PATCH] Notify (deprecated): added support for the $text$ substitution. --- src/lib-sieve/plugins/notify/cmd-notify.c | 75 +----- .../plugins/notify/ext-notify-common.c | 228 ++++++++++++++++++ .../plugins/notify/ext-notify-common.h | 8 + tests/deprecated/notify/mailto.svtest | 162 +++++++++++++ 4 files changed, 399 insertions(+), 74 deletions(-) diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c index e5939ddd1..bb539a56e 100644 --- a/src/lib-sieve/plugins/notify/cmd-notify.c +++ b/src/lib-sieve/plugins/notify/cmd-notify.c @@ -418,79 +418,6 @@ static bool cmd_notify_operation_dump * Code execution */ -static void cmd_notify_construct_message -(const struct sieve_runtime_env *renv, const char *msg_format, - string_t *out_msg) -{ - const struct sieve_message_data *msgdata = renv->msgdata; - const char *p; - - if ( msg_format == NULL ) - msg_format = "$from$: $subject$"; - - /* Scan message for substitutions */ - p = msg_format; - while ( *p != '\0' ) { - const char *const *header; - - if ( strncasecmp(p, "$from$", 6) == 0 ) { - p += 6; - - /* Fetch sender from oriinal message */ - if ( mail_get_headers_utf8(msgdata->mail, "from", &header) >= 0 ) - str_append(out_msg, header[0]); - - } else if ( strncasecmp(p, "$env-from$", 10) == 0 ) { - p += 10; - - if ( msgdata->return_path != NULL ) - str_append(out_msg, msgdata->return_path); - - } else if ( strncasecmp(p, "$subject$", 9) == 0 ) { - p += 9; - - /* Fetch sender from oriinal message */ - if ( mail_get_headers_utf8(msgdata->mail, "subject", &header) >= 0 ) - str_append(out_msg, header[0]); - - } else if ( strncasecmp(p, "$text", 5) == 0 - && (p[5] == '[' || p[5] == '$') ) { - size_t num = 0; - const char *begin = p; - bool valid = TRUE; - - p += 5; - if ( *p == '[' ) { - p += 1; - - while ( i_isdigit(*p) ) { - num = num * 10 + (*p - '0'); - p++; - } - - if ( *p++ != ']' || *p++ != '$' ) { - str_append_n(out_msg, begin, p-begin); - valid = FALSE; - } - } else { - p += 1; - } - - if ( valid ) { - str_append(out_msg, "<body extraction not supported>"); - } - } else { - size_t len; - - /* Find next substitution */ - len = strcspn(p + 1, "$") + 1; - - /* Copy normal text */ - str_append_n(out_msg, p, len); - p += len; - } - } -} static int cmd_notify_operation_execute (const struct sieve_operation *op ATTR_UNUSED, @@ -584,7 +511,7 @@ static int cmd_notify_operation_execute /* Process message */ out_message = t_str_new(1024); - cmd_notify_construct_message + ext_notify_construct_message (renv, (message == NULL ? NULL : str_c(message)), out_message); act->message = p_strdup(pool, str_c(out_message)); diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.c b/src/lib-sieve/plugins/notify/ext-notify-common.c index 1c4f8de83..b4d48c7c8 100644 --- a/src/lib-sieve/plugins/notify/ext-notify-common.c +++ b/src/lib-sieve/plugins/notify/ext-notify-common.c @@ -2,9 +2,14 @@ */ #include "lib.h" +#include "str.h" +#include "rfc822-parser.h" +#include "message-parser.h" +#include "message-decoder.h" #include "sieve-common.h" #include "sieve-code.h" +#include "sieve-message.h" #include "sieve-extensions.h" #include "sieve-commands.h" #include "sieve-actions.h" @@ -16,6 +21,8 @@ #include "ext-notify-common.h" +#include <ctype.h> + /* * Importance argument */ @@ -75,3 +82,224 @@ void ext_notify_register_importance_tags sieve_validator_register_tag(valdtr, cmd_reg, &importance_high_tag, id_code); } +/* + * Body extraction + */ + +/* FIXME: overlaps somewhat with body extension */ + +struct ext_notify_message_context { + pool_t pool; + buffer_t *body_text; +}; + +static struct ext_notify_message_context *ext_notify_get_message_context +(struct sieve_message_context *msgctx) +{ + struct ext_notify_message_context *ctx; + + /* Get message context (contains cached message body information) */ + ctx = (struct ext_notify_message_context *) + sieve_message_context_extension_get(msgctx, ¬ify_extension); + + /* Create it if it does not exist already */ + if ( ctx == NULL ) { + pool_t pool = sieve_message_context_pool(msgctx); + ctx = p_new(pool, struct ext_notify_message_context, 1); + ctx->pool = pool; + ctx->body_text = NULL; + + /* Register context */ + sieve_message_context_extension_set + (msgctx, ¬ify_extension, (void *) ctx); + } + + return ctx; +} + +static bool _is_text_content(const struct message_header_line *hdr) +{ + struct rfc822_parser_context parser; + string_t *content_type; + const char *data; + + /* Initialize parsing */ + rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); + (void)rfc822_skip_lwsp(&parser); + + /* Parse content type */ + content_type = t_str_new(64); + if (rfc822_parse_content_type(&parser, content_type) < 0) + return ""; + + /* Content-type value must end here, otherwise it is invalid after all */ + (void)rfc822_skip_lwsp(&parser); + if ( parser.data != parser.end && *parser.data != ';' ) + return ""; + + /* Success */ + data = str_c(content_type); + if ( strncmp(data, "text", 4) == 0 && data[4] == '/' ) { + return TRUE; + } + + return FALSE; +} + +static buffer_t *cmd_notify_extract_body_text +(const struct sieve_runtime_env *renv) +{ + struct ext_notify_message_context *mctx; + struct message_parser_ctx *parser; + struct message_decoder_context *decoder; + struct message_part *parts; + struct message_block block, decoded; + struct istream *input; + bool is_text, save_body; + int ret; + + /* Return cached result if available */ + mctx = ext_notify_get_message_context(renv->msgctx); + if ( mctx->body_text != NULL ) { + return mctx->body_text; + } + + /* Create buffer */ + mctx->body_text = buffer_create_dynamic(mctx->pool, 1024*64); + + /* Get the message stream */ + if ( mail_get_stream(renv->msgdata->mail, NULL, NULL, &input) < 0 ) + return FALSE; + + /* Initialize body decoder */ + decoder = message_decoder_init(FALSE); + + parser = message_parser_init(mctx->pool, input, 0, 0); + is_text = TRUE; + while ( (ret = message_parser_parse_next_block(parser, &block)) > 0 ) { + if ( block.hdr != NULL || block.size == 0 ) { + /* Decode block */ + (void)message_decoder_decode_next_block(decoder, &block, &decoded); + + /* Check for end of headers */ + if ( block.hdr == NULL ) { + save_body = is_text; + continue; + } + + /* We're interested of only Content-Type: header */ + if ( strcasecmp(block.hdr->name, "Content-Type" ) != 0) + continue; + + /* Header can have folding whitespace. Acquire the full value before + * continuing + */ + if ( block.hdr->continues ) { + block.hdr->use_full_value = TRUE; + continue; + } + + /* Is it a text part? */ + T_BEGIN { + is_text = _is_text_content(block.hdr); + } T_END; + + continue; + } + + /* Read text body */ + if ( save_body ) { + (void)message_decoder_decode_next_block(decoder, &block, &decoded); + buffer_append(mctx->body_text, decoded.data, decoded.size); + is_text = TRUE; + } + } + + /* Cleanup */ + (void)message_parser_deinit(&parser, &parts); + message_decoder_deinit(&decoder); + + /* Return status */ + return mctx->body_text; +} + +void ext_notify_construct_message +(const struct sieve_runtime_env *renv, const char *msg_format, + string_t *out_msg) +{ + const struct sieve_message_data *msgdata = renv->msgdata; + const char *p; + + if ( msg_format == NULL ) + msg_format = "$from$: $subject$"; + + /* Scan message for substitutions */ + p = msg_format; + while ( *p != '\0' ) { + const char *const *header; + + if ( strncasecmp(p, "$from$", 6) == 0 ) { + p += 6; + + /* Fetch sender from oriinal message */ + if ( mail_get_headers_utf8(msgdata->mail, "from", &header) >= 0 ) + str_append(out_msg, header[0]); + + } else if ( strncasecmp(p, "$env-from$", 10) == 0 ) { + p += 10; + + if ( msgdata->return_path != NULL ) + str_append(out_msg, msgdata->return_path); + + } else if ( strncasecmp(p, "$subject$", 9) == 0 ) { + p += 9; + + /* Fetch sender from oriinal message */ + if ( mail_get_headers_utf8(msgdata->mail, "subject", &header) >= 0 ) + str_append(out_msg, header[0]); + + } else if ( strncasecmp(p, "$text", 5) == 0 + && (p[5] == '[' || p[5] == '$') ) { + size_t num = 0; + const char *begin = p; + bool valid = TRUE; + + p += 5; + if ( *p == '[' ) { + p += 1; + + while ( i_isdigit(*p) ) { + num = num * 10 + (*p - '0'); + p++; + } + + if ( *p++ != ']' || *p++ != '$' ) { + str_append_n(out_msg, begin, p-begin); + valid = FALSE; + } + } else { + p += 1; + } + + if ( valid ) { + size_t body_size; + const char *body_text = (const char *) + buffer_get_data(cmd_notify_extract_body_text(renv), &body_size); + + if ( num > 0 && num < body_size) + str_append_n(out_msg, body_text, num); + else + str_append_n(out_msg, body_text, body_size); + } + } else { + size_t len; + + /* Find next substitution */ + len = strcspn(p + 1, "$") + 1; + + /* Copy normal text */ + str_append_n(out_msg, p, len); + p += len; + } + } +} diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.h b/src/lib-sieve/plugins/notify/ext-notify-common.h index de00e3442..146d7c6d9 100644 --- a/src/lib-sieve/plugins/notify/ext-notify-common.h +++ b/src/lib-sieve/plugins/notify/ext-notify-common.h @@ -54,4 +54,12 @@ struct ext_notify_action { ARRAY_TYPE(recipients) recipients; }; +/* + * Message construct + */ + +void ext_notify_construct_message + (const struct sieve_runtime_env *renv, const char *msg_format, + string_t *out_msg); + #endif /* __EXT_NOTIFY_COMMON_H */ diff --git a/tests/deprecated/notify/mailto.svtest b/tests/deprecated/notify/mailto.svtest index 37b83c975..ac63c9bd8 100644 --- a/tests/deprecated/notify/mailto.svtest +++ b/tests/deprecated/notify/mailto.svtest @@ -1,5 +1,7 @@ require "vnd.dovecot.testsuite"; + require "notify"; +require "body"; require "relational"; require "comparator-i;ascii-numeric"; @@ -153,3 +155,163 @@ test "Notifying on automated messages" { } } +test_result_reset; + +test_set "message" text: +To: nico@vestingbar.nl +From: stephan@rename-it.nl +Subject: Test + +Test. Test +Frop! +. +; + +test "Body; Singular Message" { + notify :low :id "frop" :options "stephan@rename-it.nl" + :message text: +Received interesting message: + +$text$ + +You have been notified. +. +; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not body :raw :contains "Received interesting message" { + test_fail "notification has no heading"; + } + + if not body :raw :contains "You have been notified" { + test_fail "notification has no footer"; + } + + if not allof( + body :raw :contains "Test. Test", + body :raw :contains "Frop" ) { + test_fail "notification has no original message"; + } +} + +test_result_reset; + +test_set "message" text: +To: nico@vestingbar.nl +From: stephan@rename-it.nl +Subject: Test + +Test. Test +Frop! +. +; + +test "Body; $text[maxsize]$" { + notify :low :id "frop" :options "sirius@rename-it.nl" + :message text: +Received interesting message: + +$text[5]$ + +You have been notified. +. +; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not body :raw :contains "Received interesting message" { + test_fail "notification has no heading"; + } + + if not body :raw :contains "You have been notified" { + test_fail "notification has no footer"; + } + + if anyof( + body :raw :contains "Test. Test", + body :raw :contains "Frop" ) { + test_fail "original message in notification is not truncated"; + } + + if not body :raw :contains "Test." { + test_fail "notification does not contain the required message"; + } +} + +test_result_reset; + +test_set "message" text: +From: Whomever <whoever@example.com> +To: Someone <someone@example.com> +Date: Sat, 10 Oct 2009 00:30:04 +0200 +Subject: whatever +Content-Type: multipart/mixed; boundary=outer + +This is a multi-part message in MIME format. + +--outer +Content-Type: multipart/alternative; boundary=inner + +This is a nested multi-part message in MIME format. + +--inner +Content-Type: application/sieve; charset="us-ascii" + +keep; + +--inner +Content-Type: text/plain; charset="us-ascii" + +Friep! + +--inner-- + +This is the end of the inner MIME multipart. + +--outer +Content-Type: message/rfc822 + +From: Someone Else +Subject: hello request + +Please say Hello + +--outer-- + +This is the end of the outer MIME multipart. +. +; + +test "Body; Multipart Message" { + notify :low :id "frop" :options "stephan@rename-it.nl" + :message text: +Received interesting message: + +$text$ + +You have been notified. +. +; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not body :raw :contains "Friep!" { + test_fail "notification has incorrect content"; + } +} + + + -- GitLab