From 750ccb616db1083beb424f6e71d524e2fd142d92 Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Sat, 1 Dec 2007 19:06:18 +0100 Subject: [PATCH] Implemented actions reject and vacation. --- sieve/tests/vacation.sieve | 2 +- src/lib-sieve/cmd-redirect.c | 51 ++- src/lib-sieve/ext-reject.c | 181 +++++++- src/lib-sieve/plugins/vacation/ext-vacation.c | 386 +++++++++++++++++- src/lib-sieve/sieve-actions.c | 14 + src/lib-sieve/sieve-actions.h | 5 + src/lib-sieve/sieve-address-parts.c | 2 +- src/lib-sieve/sieve.h | 26 +- src/sieve-bin/sieve-exec.c | 94 +++-- src/sieve-bin/sieve-test.c | 1 + 10 files changed, 706 insertions(+), 56 deletions(-) diff --git a/sieve/tests/vacation.sieve b/sieve/tests/vacation.sieve index f2565fb5f..b7e232a43 100644 --- a/sieve/tests/vacation.sieve +++ b/sieve/tests/vacation.sieve @@ -2,7 +2,7 @@ require "vacation"; if address :contains "to" "vestingbar" { - vacation :subject "I am gone" :from "sirius@drunksnipers.com" + vacation :from "sirius@drunksnipers.com" :days 30 "I am on vacation."; stop; } diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c index 2ceb9d520..608179435 100644 --- a/src/lib-sieve/cmd-redirect.c +++ b/src/lib-sieve/cmd-redirect.c @@ -1,5 +1,7 @@ #include "lib.h" #include "str-sanitize.h" +#include "istream.h" +#include "istream-header-filter.h" #include "sieve-commands.h" #include "sieve-commands-private.h" @@ -9,6 +11,8 @@ #include "sieve-interpreter.h" #include "sieve-result.h" +#include <stdio.h> + /* Forward declarations */ static bool cmd_redirect_opcode_dump @@ -188,19 +192,52 @@ static void act_redirect_print *keep = FALSE; } +static bool act_redirect_send + (const struct sieve_action_exec_env *aenv, struct act_redirect_context *ctx) +{ + const struct sieve_message_data *msgdata = aenv->msgdata; + const struct sieve_mail_environment *mailenv = aenv->mailenv; + struct istream *input; + static const char *hide_headers[] = { "Return-Path" }; + void *smtp_handle; + FILE *f; + const unsigned char *data; + size_t size; + int ret; + + /* Just to be sure */ + if ( mailenv->smtp_open == NULL || mailenv->smtp_close == NULL ) { + sieve_result_error(aenv, "redirect action has no means to send mail."); + return FALSE; + } + + if (mail_get_stream(msgdata->mail, NULL, NULL, &input) < 0) + return -1; + + smtp_handle = mailenv->smtp_open(ctx->to_address, msgdata->return_path, &f); + + input = i_stream_create_header_filter + (input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hide_headers, + N_ELEMENTS(hide_headers), null_header_filter_callback, NULL); + + while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { + if (fwrite(data, size, 1, f) == 0) + break; + i_stream_skip(input, size); + } + + return mailenv->smtp_close(smtp_handle); +} + static bool act_redirect_commit (const struct sieve_action *action ATTR_UNUSED, const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep) { - const struct sieve_message_data *msgdata = aenv->msgdata; struct act_redirect_context *ctx = (struct act_redirect_context *) tr_context; - int res; - if ((res = aenv->mailenv-> - send_forward(msgdata, ctx->to_address)) == 0) { - i_info("msgid=%s: forwarded to <%s>", - msgdata->id == NULL ? "" : str_sanitize(msgdata->id, 80), - str_sanitize(ctx->to_address, 80)); + if ( act_redirect_send(aenv, ctx) ) { + sieve_result_log(aenv, "forwarded to <%s>", + str_sanitize(ctx->to_address, 80)); *keep = FALSE; return TRUE; diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c index ff7af5004..c8ef054d7 100644 --- a/src/lib-sieve/ext-reject.c +++ b/src/lib-sieve/ext-reject.c @@ -9,12 +9,20 @@ * */ -#include <stdio.h> +#include "lib.h" +#include "ioloop.h" +#include "hostpid.h" +#include "message-date.h" +#include "message-size.h" +#include "istream.h" +#include "istream-header-filter.h" #include "sieve-extensions.h" #include "sieve-commands.h" +#include "sieve-actions.h" #include "sieve-validator.h" #include "sieve-interpreter.h" +#include "sieve-result.h" /* Forward declarations */ @@ -80,6 +88,28 @@ struct sieve_opcode reject_opcode = { ext_reject_opcode_execute }; +/* Reject action */ + +static void act_reject_print + (const struct sieve_action *action, void *context, bool *keep); +static bool act_reject_commit +(const struct sieve_action *action ATTR_UNUSED, + const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); + +struct act_reject_context { + const char *reason; +}; + +const struct sieve_action act_reject = { + "reject", + NULL, + NULL, + act_reject_print, + NULL, NULL, + act_reject_commit, + NULL +}; + /* * Validation */ @@ -144,9 +174,15 @@ static bool ext_reject_opcode_execute (const struct sieve_opcode *opcode ATTR_UNUSED, const struct sieve_runtime_env *renv, sieve_size_t *address) { + struct sieve_side_effects_list *slist = NULL; + struct act_reject_context *act; string_t *reason; + pool_t pool; t_push(); + + if ( !sieve_interpreter_handle_optional_operands(renv, address, &slist) ) + return FALSE; if ( !sieve_opr_string_read(renv->sbin, address, &reason) ) { t_pop(); @@ -155,7 +191,150 @@ static bool ext_reject_opcode_execute printf(">> REJECT \"%s\"\n", str_c(reason)); + /* Add reject action to the result */ + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct act_reject_context, 1); + act->reason = p_strdup(pool, str_c(reason)); + + (void) sieve_result_add_action(renv, &act_reject, slist, (void *) act); + t_pop(); return TRUE; } +/* + * Action + */ + +static void act_reject_print +(const struct sieve_action *action ATTR_UNUSED, void *context, bool *keep) +{ + struct act_reject_context *ctx = (struct act_reject_context *) context; + + printf("* reject message with reason: %s\n", ctx->reason); + + *keep = FALSE; +} + +static bool act_reject_send + (const struct sieve_action_exec_env *aenv, struct act_reject_context *ctx) +{ + const struct sieve_message_data *msgdata = aenv->msgdata; + const struct sieve_mail_environment *mailenv = aenv->mailenv; + struct istream *input; + void *smtp_handle; + struct message_size hdr_size; + FILE *f; + const char *new_msgid, *boundary; + const unsigned char *data; + const char *header; + size_t size; + int ret; + + /* Just to be sure */ + if ( mailenv->smtp_open == NULL || mailenv->smtp_close == NULL ) { + sieve_result_error(aenv, "reject action has no means to send mail."); + return FALSE; + } + + smtp_handle = mailenv->smtp_open(msgdata->return_path, NULL, &f); + + new_msgid = sieve_get_new_message_id(mailenv); + boundary = t_strdup_printf("%s/%s", my_pid, mailenv->hostname); + + fprintf(f, "Message-ID: %s\r\n", new_msgid); + fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time)); + fprintf(f, "From: Mail Delivery Subsystem <%s>\r\n", + mailenv->postmaster_address); + fprintf(f, "To: <%s>\r\n", msgdata->return_path); + fprintf(f, "MIME-Version: 1.0\r\n"); + fprintf(f, "Content-Type: " + "multipart/report; report-type=disposition-notification;\r\n" + "\tboundary=\"%s\"\r\n", boundary); + fprintf(f, "Subject: Automatically rejected mail\r\n"); + fprintf(f, "Auto-Submitted: auto-replied (rejected)\r\n"); + fprintf(f, "Precedence: bulk\r\n"); + fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); + + /* Human readable status report */ + fprintf(f, "--%s\r\n", boundary); + fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n"); + fprintf(f, "Content-Disposition: inline\r\n"); + fprintf(f, "Content-Transfer-Encoding: 8bit\r\n\r\n"); + + /* FIXME: var_expand_table expansion not possible */ + fprintf(f, "Your message to <%s> was automatically rejected:\r\n" + "%s\r\n", msgdata->to_address, ctx->reason); + + /* MDN status report */ + fprintf(f, "--%s\r\n" + "Content-Type: message/disposition-notification\r\n\r\n", boundary); + fprintf(f, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n", + mailenv->hostname); + if (mail_get_first_header(msgdata->mail, "Original-Recipient", &header) > 0) + fprintf(f, "Original-Recipient: rfc822; %s\r\n", header); + fprintf(f, "Final-Recipient: rfc822; %s\r\n", msgdata->to_address); + + if ( msgdata->id != NULL ) + fprintf(f, "Original-Message-ID: %s\r\n", msgdata->id); + fprintf(f, "Disposition: " + "automatic-action/MDN-sent-automatically; deleted\r\n"); + fprintf(f, "\r\n"); + + /* original message's headers */ + fprintf(f, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary); + + if (mail_get_stream(msgdata->mail, &hdr_size, NULL, &input) == 0) { + /* Note: If you add more headers, they need to be sorted. + We'll drop Content-Type because we're not including the message + body, and having a multipart Content-Type may confuse some + MIME parsers when they don't see the message boundaries. */ + static const char *const exclude_headers[] = { + "Content-Type" + }; + + input = i_stream_create_header_filter(input, + HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | HEADER_FILTER_HIDE_BODY, + exclude_headers, N_ELEMENTS(exclude_headers), + null_header_filter_callback, NULL); + + while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { + if (fwrite(data, size, 1, f) == 0) + break; + i_stream_skip(input, size); + } + i_stream_unref(&input); + + i_assert(ret != 0); + } + + fprintf(f, "\r\n\r\n--%s--\r\n", boundary); + + return mailenv->smtp_close(smtp_handle); +} + +static bool act_reject_commit +(const struct sieve_action *action ATTR_UNUSED, + const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep) +{ + const struct sieve_message_data *msgdata = aenv->msgdata; + struct act_reject_context *ctx = (struct act_reject_context *) tr_context; + + if ( msgdata->return_path == NULL || *(msgdata->return_path) == '\0' ) { + sieve_result_log(aenv, "discarded reject to <>"); + + *keep = FALSE; + return TRUE; + } + + if ( act_reject_send(aenv, ctx) ) { + sieve_result_log(aenv, "rejected"); + + *keep = FALSE; + return TRUE; + } + + return FALSE; +} + + diff --git a/src/lib-sieve/plugins/vacation/ext-vacation.c b/src/lib-sieve/plugins/vacation/ext-vacation.c index 9d2cd138f..b3f749cd2 100644 --- a/src/lib-sieve/plugins/vacation/ext-vacation.c +++ b/src/lib-sieve/plugins/vacation/ext-vacation.c @@ -9,16 +9,27 @@ * */ -#include <stdio.h> +#include "lib.h" +#include "md5.h" +#include "hostpid.h" +#include "str-sanitize.h" +#include "message-address.h" +#include "message-date.h" +#include "ioloop.h" #include "sieve-common.h" #include "sieve-code.h" #include "sieve-extensions.h" #include "sieve-commands.h" +#include "sieve-actions.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-interpreter.h" +#include "sieve-result.h" + +#include <stdio.h> + /* Forward declarations */ @@ -89,6 +100,35 @@ const struct sieve_opcode vacation_opcode = { ext_vacation_opcode_execute }; +/* Vacation action */ + +static void act_vacation_print + (const struct sieve_action *action, void *context, bool *keep); +static bool act_vacation_commit +(const struct sieve_action *action ATTR_UNUSED, + const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); + +struct act_vacation_context { + const char *reason; + + sieve_size_t days; + const char *subject; + const char *handle; + bool mime; + const char *from; + const char *const *addresses; +}; + +const struct sieve_action act_vacation = { + "vacation", + NULL, + NULL, + act_vacation_print, + NULL, NULL, + act_vacation_commit, + NULL +}; + /* Tag validation */ static bool cmd_vacation_validate_number_tag @@ -338,9 +378,12 @@ static bool ext_vacation_opcode_execute (const struct sieve_opcode *opcode ATTR_UNUSED, const struct sieve_runtime_env *renv, sieve_size_t *address) { + struct sieve_side_effects_list *slist = NULL; + struct act_vacation_context *act; + pool_t pool; int opt_code = 1; - sieve_size_t days = 0; - string_t *reason, *subject, *from, *handle; + sieve_size_t days = 7; + string_t *reason, *subject = NULL, *from = NULL, *handle = NULL; if ( sieve_operand_optional_present(renv->sbin, address) ) { while ( opt_code != 0 ) { @@ -378,7 +421,344 @@ static bool ext_vacation_opcode_execute printf(">> VACATION \"%s\"\n", str_c(reason)); + /* Add vacation action to the result */ + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct act_vacation_context, 1); + act->reason = p_strdup(pool, str_c(reason)); + if ( subject != NULL ) + act->subject = p_strdup(pool, str_c(subject)); + if ( from != NULL ) + act->from = p_strdup(pool, str_c(from)); + if ( handle != NULL ) + act->handle = p_strdup(pool, str_c(handle)); + act->days = days; + + /* FIXME: :addresses is ignored */ + + (void) sieve_result_add_action(renv, &act_vacation, slist, (void *) act); + return TRUE; } +/* + * Action + */ + +static void act_vacation_print +(const struct sieve_action *action ATTR_UNUSED, void *context, + bool *keep ATTR_UNUSED) +{ + struct act_vacation_context *ctx = (struct act_vacation_context *) context; + + printf( "* send vacation message:\n" + " => days : %d\n", ctx->days); + if ( ctx->subject != NULL ) + printf( " => subject: %s\n", ctx->subject); + if ( ctx->from != NULL ) + printf( " => from : %s\n", ctx->from); + if ( ctx->handle != NULL ) + printf( " => handle : %s\n", ctx->handle); + printf( "\nSTART MESSAGE\n%s\nEND MESSAGE\n", ctx->reason); +} + +static const char * const _list_headers[] = { + "list-id", + "list-owner", + "list-subscribe", + "list-post", + "list-unsubscribe", + "list-help", + "list-archive", + NULL +}; + +static const char * const _my_address_headers[] = { + "to", + "cc", + "bcc", + "resent-to", + "resent-cc", + "resent-bcc", + NULL +}; + +static inline bool _is_system_address(const char *address) +{ + if ( strncasecmp(address, "MAILER-DAEMON", 13) == 0 ) + return TRUE; + + if ( strncasecmp(address, "LISTSERV", 8) == 0 ) + return TRUE; + + if ( strncasecmp(address, "majordomo", 9) == 0 ) + return TRUE; + + if ( strstr(address, "-request@") != NULL ) + return TRUE; + + if ( strncmp(address, "owner-", 6) == 0 ) + return TRUE; + + return FALSE; +} + +static inline bool _contains_my_address + (const char * const *headers, const char *my_address) +{ + const char *const *hdsp = headers; + + while ( *hdsp != NULL ) { + const struct message_address *addr; + + t_push(); + + addr = message_address_parse + (pool_datastack_create(), (const unsigned char *) *hdsp, + strlen(*hdsp), 256, FALSE); + + while ( addr != NULL ) { + if (addr->domain != NULL) { + i_assert(addr->mailbox != NULL); + + if ( strcmp(t_strconcat(addr->mailbox, "@", addr->domain, NULL), + my_address) == 0 ) { + t_pop(); + return TRUE; + } + } + + addr = addr->next; + } + + t_pop(); + + hdsp++; + } + + return FALSE; +} + +static bool act_vacation_send + (const struct sieve_action_exec_env *aenv, struct act_vacation_context *ctx) +{ + const struct sieve_message_data *msgdata = aenv->msgdata; + const struct sieve_mail_environment *mailenv = aenv->mailenv; + void *smtp_handle; + FILE *f; + const char *outmsgid; + + /* Just to be sure */ + if ( mailenv->smtp_open == NULL || mailenv->smtp_close == NULL ) { + sieve_result_error(aenv, "vacation action has no means to send mail."); + return FALSE; + } + + smtp_handle = mailenv->smtp_open(msgdata->return_path, NULL, &f); + outmsgid = sieve_get_new_message_id(mailenv); + + fprintf(f, "Message-ID: %s\r\n", outmsgid); + fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time)); + if ( ctx->from != NULL && *(ctx->from) != '\0' ) + fprintf(f, "From: <%s>\r\n", ctx->from); + else + fprintf(f, "From: <%s>\r\n", msgdata->to_address); + + fprintf(f, "To: <%s>\r\n", msgdata->return_path); + fprintf(f, "Subject: %s\r\n", str_sanitize(ctx->subject, 80)); + if ( msgdata->id != NULL ) + fprintf(f, "In-Reply-To: %s\r\n", msgdata->id); + fprintf(f, "Auto-Submitted: auto-replied (vacation)\r\n"); + + /* FIXME: What about the required references header ? */ + + fprintf(f, "X-Sieve: %s\r\n", SIEVE_IMPLEMENTATION); + + fprintf(f, "Precedence: bulk\r\n"); + fprintf(f, "MIME-Version: 1.0\r\n"); + + if ( ctx->mime ) { + fprintf(f, "Content-Type: multipart/mixed;" + "\r\n\tboundary=\"%s/%s\"\r\n", my_pid, mailenv->hostname); + fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); + fprintf(f, "--%s/%s\r\n", my_pid, mailenv->hostname); + } else { + fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n"); + fprintf(f, "Content-Transfer-Encoding: 8bit\r\n"); + fprintf(f, "\r\n"); + } + + fprintf(f, "%s\r\n", ctx->reason); + + if ( ctx->mime ) + fprintf(f, "\r\n--%s/%s--\r\n", my_pid, mailenv->hostname); + + if ( mailenv->smtp_close(smtp_handle) ) { + /*mailenv->duplicate_mark(outmsgid, strlen(outmsgid), + mailenv->username, ioloop_time + DUPLICATE_DEFAULT_KEEP);*/ + return TRUE; + } + + return FALSE; +} + +static void act_vacation_hash +(const struct sieve_message_data *msgdata, struct act_vacation_context *vctx, + unsigned char hash_r[]) +{ + struct md5_context ctx; + + md5_init(&ctx); + md5_update(&ctx, msgdata->return_path, strlen(msgdata->return_path)); + + if ( vctx->handle != NULL && *(vctx->handle) != '\0' ) + md5_update(&ctx, vctx->handle, strlen(vctx->handle)); + else { + md5_update(&ctx, msgdata->to_address, strlen(msgdata->to_address)); + md5_update(&ctx, vctx->reason, strlen(vctx->reason)); + } + + md5_final(&ctx, hash_r); +} + +static bool act_vacation_commit +(const struct sieve_action *action ATTR_UNUSED, + const struct sieve_action_exec_env *aenv, void *tr_context, + bool *keep ATTR_UNUSED) +{ + const char *const *hdsp; + const struct sieve_message_data *msgdata = aenv->msgdata; + const struct sieve_mail_environment *mailenv = aenv->mailenv; + struct act_vacation_context *ctx = (struct act_vacation_context *) tr_context; + unsigned char dupl_hash[MD5_RESULTLEN]; + const char *const *headers; + pool_t pool; + + /* Is the return_path unset ? + */ + if ( msgdata->return_path == NULL || *(msgdata->return_path) == '\0' ) { + sieve_result_log(aenv, "discarded vacation reply to <>"); + return TRUE; + } + + /* Are we perhaps trying to respond to ourselves ? + * (FIXME: verify this to :addresses as well) + */ + if ( strcmp(msgdata->return_path, msgdata->to_address) == 0 ) { + sieve_result_log(aenv, "discarded vacation reply to own address"); + return TRUE; + } + + /* Did whe respond to this user before? */ + if (mailenv->duplicate_check(dupl_hash, sizeof(dupl_hash), mailenv->username)) + { + sieve_result_log(aenv, "discarded duplicate vacation response to <%s>", + str_sanitize(msgdata->return_path, 80)); + return TRUE; + } + + /* Are we trying to respond to a mailing list ? */ + hdsp = _list_headers; + while ( *hdsp != NULL ) { + if ( mail_get_headers_utf8 + (msgdata->mail, *hdsp, &headers) >= 0 && headers[0] != NULL ) { + /* Yes, bail out */ + sieve_result_log(aenv, + "discarding vacation response to mailinglist recipient <%s>", + msgdata->return_path); + return TRUE; + } + hdsp++; + } + + /* Is the message that we are replying to an automatic reply ? */ + if ( mail_get_headers_utf8 + (msgdata->mail, "auto-submitted", &headers) >= 0 ) { + /* Theoretically multiple headers could exist, so lets make sure */ + hdsp = headers; + while ( *hdsp != NULL ) { + if ( strcasecmp(*hdsp, "no") != 0 ) { + sieve_result_log(aenv, + "discardig vacation response to auto-submitted message from <%s>", + msgdata->return_path); + return TRUE; + } + hdsp++; + } + } + + /* Check for non-standard precedence header */ + if ( mail_get_headers_utf8 + (msgdata->mail, "precedence", &headers) >= 0 ) { + /* Theoretically multiple headers could exist, so lets make sure */ + hdsp = headers; + while ( *hdsp != NULL ) { + if ( strcasecmp(*hdsp, "junk") == 0 || strcasecmp(*hdsp, "bulk") == 0 || + strcasecmp(*hdsp, "list") == 0 ) { + sieve_result_log(aenv, + "discarding vacation response to precedence=%s message from <%s>", + *hdsp, msgdata->return_path); + return TRUE; + } + hdsp++; + } + } + + /* Do not reply to system addresses */ + if ( _is_system_address(msgdata->return_path) ) { + sieve_result_log(aenv, + "not sending vacation response to system address <%s>", + msgdata->return_path); + return TRUE; + } + + /* Is the original message directly addressed to me? */ + hdsp = _my_address_headers; + while ( *hdsp != NULL ) { + if ( mail_get_headers_utf8 + (msgdata->mail, *hdsp, &headers) >= 0 && headers[0] != NULL ) { + + if ( _contains_my_address(headers, msgdata->to_address) ) + break; + } + hdsp++; + } + + if ( *hdsp == NULL ) { + /* No, bail out */ + sieve_result_log(aenv, + "discarding vacation response for implicitly delivered message", + msgdata->return_path); + return TRUE; + } + + /* Make sure we have a subject for our reply */ + if ( ctx->subject == NULL || *(ctx->subject) == '\0' ) { + if ( mail_get_headers_utf8 + (msgdata->mail, "subject", &headers) >= 0 && headers[0] != NULL ) { + pool = sieve_result_pool(aenv->result); + ctx->subject = p_strconcat(pool, "Auto: ", headers[0], NULL); + } else { + ctx->subject = "Automated reply"; + } + } + + /* Send the message */ + + if ( act_vacation_send(aenv, ctx) ) { + sieve_result_log(aenv, "sent vacation response to <%s>", + str_sanitize(msgdata->return_path, 80)); + + mailenv->duplicate_mark(dupl_hash, sizeof(dupl_hash), mailenv->username, + ioloop_time + ctx->days * (24 * 60 * 60)); + + return TRUE; + } + + sieve_result_error(aenv, "failed to send vacation response to <%s>", + str_sanitize(msgdata->return_path, 80)); + return FALSE; +} + + + diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c index 7a2c73a25..6590abc89 100644 --- a/src/lib-sieve/sieve-actions.c +++ b/src/lib-sieve/sieve-actions.c @@ -1,4 +1,5 @@ #include "lib.h" +#include "ioloop.h" #include "str-sanitize.h" #include "mail-storage.h" #include "mail-namespace.h" @@ -12,6 +13,19 @@ #include <ctype.h> +/* + * Message transmission (FIXME: place this somewhere more appropriate) + */ +const char *sieve_get_new_message_id + (const struct sieve_mail_environment *mailenv) +{ + static int count = 0; + + return t_strdup_printf("<dovecot-sieve-%s-%s-%d@%s>", + dec2str(ioloop_timeval.tv_sec), dec2str(ioloop_timeval.tv_usec), + count++, mailenv->hostname); +} + /* * Side-effects 'extension' */ diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h index c3f97bd10..91548f3ae 100644 --- a/src/lib-sieve/sieve-actions.h +++ b/src/lib-sieve/sieve-actions.h @@ -128,5 +128,10 @@ bool sieve_act_store_add_to_result (const struct sieve_runtime_env *renv, struct sieve_side_effects_list *seffects, const char *folder); +/* Message transmission */ + +const char *sieve_get_new_message_id + (const struct sieve_mail_environment *mailenv); + #endif /* __SIEVE_ACTIONS_H */ diff --git a/src/lib-sieve/sieve-address-parts.c b/src/lib-sieve/sieve-address-parts.c index f8155eb23..a48046766 100644 --- a/src/lib-sieve/sieve-address-parts.c +++ b/src/lib-sieve/sieve-address-parts.c @@ -347,7 +347,7 @@ bool sieve_address_match t_push(); addr = message_address_parse - (unsafe_data_stack_pool, (const unsigned char *) data, + (pool_datastack_create(), (const unsigned char *) data, strlen(data), 256, FALSE); while (!matched && addr != NULL) { diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h index 97dd8e51a..2516185b6 100644 --- a/src/lib-sieve/sieve.h +++ b/src/lib-sieve/sieve.h @@ -4,6 +4,11 @@ #include "lib.h" #include "mail-storage.h" +#include <stdio.h> + +#define SIEVE_VERSION "0.0.1" +#define SIEVE_IMPLEMENTATION "Dovecot Sieve " SIEVE_VERSION + struct sieve_binary; struct sieve_message_data { @@ -18,12 +23,21 @@ struct sieve_mail_environment { const char *inbox; struct mail_namespace *namespaces; - /* Interface for sending mail (callbacks if you like) */ - int (*send_rejection) - (const struct sieve_message_data *msgdata, const char *recipient, - const char *reason); - int (*send_forward) - (const struct sieve_message_data *msgdata, const char *forwardto); + const char *username; + const char *hostname; + const char *postmaster_address; + + /* Callbacks */ + + /* Interface for sending mail */ + void *(*smtp_open) + (const char *destination, const char *return_path, FILE **file_r); + bool (*smtp_close)(void *handle); + + /* Interface for marking and checking duplicates */ + int (*duplicate_check)(const void *id, size_t id_size, const char *user); + void (*duplicate_mark)(const void *id, size_t id_size, + const char *user, time_t time); }; bool sieve_init(const char *plugins); diff --git a/src/sieve-bin/sieve-exec.c b/src/sieve-bin/sieve-exec.c index 612154e96..447e2d0a6 100644 --- a/src/sieve-bin/sieve-exec.c +++ b/src/sieve-bin/sieve-exec.c @@ -20,28 +20,43 @@ #define DEFAULT_SENDMAIL_PATH "/usr/lib/sendmail" #define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON" -static int sieve_send_rejection -(const struct sieve_message_data *msgdata ATTR_UNUSED, - const char *recipient, const char *reason) +static void *sieve_smtp_open(const char *destination, + const char *return_path, FILE **file_r) +{getenv("HOSTNAME"); + printf("Sending mesage from <%s> to <%s>:\n\nSTART MESSAGE:\n", + return_path == NULL || *return_path == '\0' ? "" : return_path, + destination); + + *file_r = stdout; + + return NULL; +} + +static bool sieve_smtp_close(void *handle ATTR_UNUSED) { - i_info("<<NOT PERFORMED>> Rejected mail to %s with reason \"%s\"\n", - recipient, reason); - return 0; + printf("END MESSAGE\n\n"); + return TRUE; } -static int sieve_send_forward -(const struct sieve_message_data *msgdata ATTR_UNUSED, - const char *forwardto) +static int duplicate_check(const void *id ATTR_UNUSED, size_t id_size ATTR_UNUSED, + const char *user) { - i_info("<<NOT PERFORMED>> Forwarded mail to %s.", forwardto); + printf("Checked duplicate for user %s.\n", user); return 0; } +static void duplicate_mark +(const void *id ATTR_UNUSED, size_t id_size ATTR_UNUSED, const char *user, + time_t time ATTR_UNUSED) +{ + printf("Marked duplicate for user %s.\n", user); +} + int main(int argc, char **argv) { const char *user; - struct passwd *pw; - uid_t process_euid; + struct passwd *pw; + uid_t process_euid; int sfd, mfd; pool_t namespaces_pool; struct mail_namespace *ns; @@ -57,7 +72,7 @@ int main(int argc, char **argv) exit(1); } - /* Open sieve script */ + /* Open sieve script */ if ( (sfd = open(argv[1], O_RDONLY)) < 0 ) { perror("open()"); @@ -67,17 +82,17 @@ int main(int argc, char **argv) /* Open mail file */ if ( argc > 2 ) - { - if ( strcmp(argv[2], "-") == 0 ) - mfd = 0; - else { - if ( (mfd = open(argv[2], O_RDONLY)) < 0 ) { - perror("Failed to open mail file"); - exit(1); - } - } - } else - mfd = 0; + { + if ( strcmp(argv[2], "-") == 0 ) + mfd = 0; + else { + if ( (mfd = open(argv[2], O_RDONLY)) < 0 ) { + perror("Failed to open mail file"); + exit(1); + } + } + } else + mfd = 0; /* Compile sieve script */ @@ -97,14 +112,14 @@ int main(int argc, char **argv) close(sfd); - process_euid = geteuid(); - pw = getpwuid(process_euid); - if (pw != NULL) { - user = t_strdup(pw->pw_name); - } else { - i_fatal("Couldn't lookup our username (uid=%s)", - dec2str(process_euid)); - } + process_euid = geteuid(); + pw = getpwuid(process_euid); + if (pw != NULL) { + user = t_strdup(pw->pw_name); + } else { + i_fatal("Couldn't lookup our username (uid=%s)", + dec2str(process_euid)); + } env_put(t_strdup_printf("NAMESPACE_1=%s", "maildir:/home/stephan/Maildir")); env_put("NAMESPACE_1_INBOX=1"); @@ -115,7 +130,7 @@ int main(int argc, char **argv) namespaces_pool = namespaces_init(); if (mail_namespaces_init(namespaces_pool, user, &ns) < 0) - i_fatal("Namespace initialization failed"); + i_fatal("Namespace initialization failed"); mail_raw_init(namespaces_pool, user); mailr = mail_raw_open(mfd); @@ -124,15 +139,20 @@ int main(int argc, char **argv) memset(&msgdata, 0, sizeof(msgdata)); msgdata.mail = mailr->mail; msgdata.return_path = "nico@example.com"; - msgdata.to_address = "sirius+sieve@rename-it.nl"; - msgdata.auth_user = "stephan"; + msgdata.to_address = "sirius@rename-it.nl"; + msgdata.auth_user = "nico"; (void)mail_get_first_header(mailr->mail, "Message-ID", &msgdata.id); memset(&mailenv, 0, sizeof(mailenv)); mailenv.inbox = "INBOX"; mailenv.namespaces = ns; - mailenv.send_forward = sieve_send_forward; - mailenv.send_rejection = sieve_send_rejection; + mailenv.username = "stephan"; + mailenv.hostname = "host.example.com"; + mailenv.postmaster_address = "postmaster@example.com"; + mailenv.smtp_open = sieve_smtp_open; + mailenv.smtp_close = sieve_smtp_close; + mailenv.duplicate_mark = duplicate_mark; + mailenv.duplicate_check = duplicate_check; /* Run */ sieve_execute(sbin, &msgdata, &mailenv); diff --git a/src/sieve-bin/sieve-test.c b/src/sieve-bin/sieve-test.c index 3a60aaa09..d62b28992 100644 --- a/src/sieve-bin/sieve-test.c +++ b/src/sieve-bin/sieve-test.c @@ -98,6 +98,7 @@ int main(int argc, char **argv) memset(&mailenv, 0, sizeof(mailenv)); mailenv.inbox = "INBOX"; + mailenv.username = "stephan"; /* Run the test */ (void) sieve_test(sbin, &msgdata, &mailenv); -- GitLab