summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c')
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c794
1 files changed, 794 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
new file mode 100644
index 0000000..4e104d1
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
@@ -0,0 +1,794 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Notify method mailto
+ * --------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5436
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and
+ * draft-duerst-mailto-bis-05.txt. Should fully migrate to new specification
+ * when it matures. This requires modifications to the address parser (no
+ * whitespace allowed within the address itself) and UTF-8 support will be
+ * required in the URL.
+ */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "ioloop.h"
+#include "str-sanitize.h"
+#include "ostream.h"
+#include "message-date.h"
+#include "mail-storage.h"
+
+#include "sieve-common.h"
+#include "sieve-address.h"
+#include "sieve-address-source.h"
+#include "sieve-message.h"
+#include "sieve-smtp.h"
+#include "sieve-settings.h"
+
+#include "sieve-ext-enotify.h"
+
+#include "rfc2822.h"
+
+#include "uri-mailto.h"
+
+/*
+ * Configuration
+ */
+
+#define NTFY_MAILTO_MAX_RECIPIENTS 8
+#define NTFY_MAILTO_MAX_HEADERS 16
+#define NTFY_MAILTO_MAX_SUBJECT 256
+
+/*
+ * Mailto notification configuration
+ */
+
+struct ntfy_mailto_config {
+ pool_t pool;
+ struct sieve_address_source envelope_from;
+};
+
+/*
+ * Mailto notification method
+ */
+
+static bool ntfy_mailto_load
+ (const struct sieve_enotify_method *nmth, void **context);
+static void ntfy_mailto_unload
+ (const struct sieve_enotify_method *nmth);
+
+static bool ntfy_mailto_compile_check_uri
+ (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body);
+static bool ntfy_mailto_compile_check_from
+ (const struct sieve_enotify_env *nenv, string_t *from);
+
+static const char *ntfy_mailto_runtime_get_notify_capability
+ (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body,
+ const char *capability);
+static bool ntfy_mailto_runtime_check_uri
+ (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body);
+static bool ntfy_mailto_runtime_check_operands
+ (const struct sieve_enotify_env *nenv, const char *uri,const char *uri_body,
+ string_t *message, string_t *from, pool_t context_pool,
+ void **method_context);
+
+static int ntfy_mailto_action_check_duplicates
+ (const struct sieve_enotify_env *nenv,
+ const struct sieve_enotify_action *nact,
+ const struct sieve_enotify_action *nact_other);
+
+static void ntfy_mailto_action_print
+ (const struct sieve_enotify_print_env *penv,
+ const struct sieve_enotify_action *nact);
+
+static int ntfy_mailto_action_execute
+ (const struct sieve_enotify_exec_env *nenv,
+ const struct sieve_enotify_action *nact);
+
+const struct sieve_enotify_method_def mailto_notify = {
+ "mailto",
+ ntfy_mailto_load,
+ ntfy_mailto_unload,
+ ntfy_mailto_compile_check_uri,
+ NULL,
+ ntfy_mailto_compile_check_from,
+ NULL,
+ ntfy_mailto_runtime_check_uri,
+ ntfy_mailto_runtime_get_notify_capability,
+ ntfy_mailto_runtime_check_operands,
+ NULL,
+ ntfy_mailto_action_check_duplicates,
+ ntfy_mailto_action_print,
+ ntfy_mailto_action_execute
+};
+
+/*
+ * Reserved and unique headers
+ */
+
+static const char *_reserved_headers[] = {
+ "auto-submitted",
+ "received",
+ "message-id",
+ "data",
+ "bcc",
+ "in-reply-to",
+ "references",
+ "resent-date",
+ "resent-from",
+ "resent-sender",
+ "resent-to",
+ "resent-cc",
+ "resent-bcc",
+ "resent-msg-id",
+ "from",
+ "sender",
+ NULL
+};
+
+static const char *_unique_headers[] = {
+ "reply-to",
+ NULL
+};
+
+/*
+ * Method context data
+ */
+
+struct ntfy_mailto_context {
+ struct uri_mailto *uri;
+ const struct smtp_address *from_address;
+};
+
+/*
+ * Method registration
+ */
+
+static bool ntfy_mailto_load
+(const struct sieve_enotify_method *nmth, void **context)
+{
+ struct sieve_instance *svinst = nmth->svinst;
+ struct ntfy_mailto_config *config;
+ pool_t pool;
+
+ if ( *context != NULL ) {
+ ntfy_mailto_unload(nmth);
+ }
+
+ pool = pool_alloconly_create("ntfy_mailto_config", 256);
+ config = p_new(pool, struct ntfy_mailto_config, 1);
+ config->pool = pool;
+
+ (void)sieve_address_source_parse_from_setting (svinst,
+ config->pool, "sieve_notify_mailto_envelope_from",
+ &config->envelope_from);
+
+ *context = (void *) config;
+
+ return TRUE;
+}
+
+static void ntfy_mailto_unload
+(const struct sieve_enotify_method *nmth)
+{
+ struct ntfy_mailto_config *config =
+ (struct ntfy_mailto_config *) nmth->context;
+
+ pool_unref(&config->pool);
+}
+
+/*
+ * URI parsing
+ */
+
+struct ntfy_mailto_uri_env {
+ const struct sieve_enotify_env *nenv;
+
+ struct event *event;
+
+ struct uri_mailto_log uri_log;
+};
+
+static void ATTR_FORMAT(5, 0)
+ntfy_mailto_uri_logv(void *context, enum log_type log_type,
+ const char *csrc_filename, unsigned int csrc_linenum,
+ const char *fmt, va_list args)
+{
+ struct ntfy_mailto_uri_env *nmuenv = context;
+ const struct sieve_enotify_env *nenv = nmuenv->nenv;
+
+ sieve_event_logv(nenv->svinst, nenv->ehandler, nmuenv->event,
+ log_type, csrc_filename, csrc_linenum,
+ nenv->location, 0, fmt, args);
+}
+
+static void
+ntfy_mailto_uri_env_init(struct ntfy_mailto_uri_env *nmuenv,
+ const struct sieve_enotify_env *nenv)
+{
+ i_zero(nmuenv);
+ nmuenv->nenv = nenv;
+ nmuenv->event = event_create(nenv->event);
+ event_set_append_log_prefix(nmuenv->event, "mailto URI: ");
+
+ nmuenv->uri_log.context = nmuenv;
+ nmuenv->uri_log.logv = ntfy_mailto_uri_logv;
+}
+
+static void
+ntfy_mailto_uri_env_deinit(struct ntfy_mailto_uri_env *nmuenv)
+{
+ event_unref(&nmuenv->event);
+}
+
+/*
+ * Validation
+ */
+
+
+static bool ntfy_mailto_compile_check_uri
+(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED,
+ const char *uri_body)
+{
+ struct ntfy_mailto_uri_env nmuenv;
+ bool result;
+
+ ntfy_mailto_uri_env_init(&nmuenv, nenv);
+ result = uri_mailto_validate(
+ uri_body, _reserved_headers, _unique_headers,
+ NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS,
+ &nmuenv.uri_log);
+ ntfy_mailto_uri_env_deinit(&nmuenv);
+
+ return result;
+}
+
+static bool ntfy_mailto_compile_check_from
+(const struct sieve_enotify_env *nenv, string_t *from)
+{
+ const char *error;
+ bool result = FALSE;
+
+ T_BEGIN {
+ result = sieve_address_validate_str(from, &error);
+ if ( !result ) {
+ sieve_enotify_error(nenv,
+ "specified :from address '%s' is invalid for "
+ "the mailto method: %s",
+ str_sanitize(str_c(from), 128), error);
+ }
+ } T_END;
+
+ return result;
+}
+
+/*
+ * Runtime
+ */
+
+struct ntfy_mailto_runtime_env {
+ const struct sieve_enotify_env *nenv;
+
+ struct event *event;
+};
+
+static const char *ntfy_mailto_runtime_get_notify_capability
+(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED,
+ const char *uri_body, const char *capability)
+{
+ if ( !uri_mailto_validate(uri_body, _reserved_headers, _unique_headers,
+ NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL) ) {
+ return NULL;
+ }
+
+ if ( strcasecmp(capability, "online") == 0 )
+ return "maybe";
+
+ return NULL;
+}
+
+static bool ntfy_mailto_runtime_check_uri
+(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED,
+ const char *uri_body)
+{
+ return uri_mailto_validate
+ (uri_body, _reserved_headers, _unique_headers,
+ NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL);
+}
+
+static bool ntfy_mailto_runtime_check_operands
+(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED,
+ const char *uri_body, string_t *message ATTR_UNUSED, string_t *from,
+ pool_t context_pool, void **method_context)
+{
+ struct ntfy_mailto_context *mtctx;
+ struct uri_mailto *parsed_uri;
+ const struct smtp_address *address;
+ struct ntfy_mailto_uri_env nmuenv;
+ const char *error;
+
+ /* Need to create context before validation to have arrays present */
+ mtctx = p_new(context_pool, struct ntfy_mailto_context, 1);
+
+ /* Validate :from */
+ if ( from != NULL ) {
+ T_BEGIN {
+ address = sieve_address_parse_str(from, &error);
+ if ( address == NULL ) {
+ sieve_enotify_error(nenv,
+ "specified :from address '%s' is invalid for "
+ "the mailto method: %s",
+ str_sanitize(str_c(from), 128), error);
+ } else
+ mtctx->from_address =
+ smtp_address_clone(context_pool, address);
+ } T_END;
+
+ if ( address == NULL ) return FALSE;
+ }
+
+ ntfy_mailto_uri_env_init(&nmuenv, nenv);
+ parsed_uri = uri_mailto_parse(uri_body, context_pool,
+ _reserved_headers, _unique_headers,
+ NTFY_MAILTO_MAX_RECIPIENTS,
+ NTFY_MAILTO_MAX_HEADERS,
+ &nmuenv.uri_log);
+ ntfy_mailto_uri_env_deinit(&nmuenv);
+
+ if (parsed_uri == NULL)
+ return FALSE;
+
+ mtctx->uri = parsed_uri;
+ *method_context = (void *) mtctx;
+ return TRUE;
+}
+
+/*
+ * Action duplicates
+ */
+
+static int ntfy_mailto_action_check_duplicates
+(const struct sieve_enotify_env *nenv ATTR_UNUSED,
+ const struct sieve_enotify_action *nact,
+ const struct sieve_enotify_action *nact_other)
+{
+ struct ntfy_mailto_context *mtctx =
+ (struct ntfy_mailto_context *) nact->method_context;
+ struct ntfy_mailto_context *mtctx_other =
+ (struct ntfy_mailto_context *) nact_other->method_context;
+ const struct uri_mailto_recipient *new_rcpts, *old_rcpts;
+ unsigned int new_count, old_count, i, j;
+ unsigned int del_start = 0, del_len = 0;
+
+ new_rcpts = array_get(&mtctx->uri->recipients, &new_count);
+ old_rcpts = array_get(&mtctx_other->uri->recipients, &old_count);
+
+ for ( i = 0; i < new_count; i++ ) {
+ for ( j = 0; j < old_count; j++ ) {
+ if ( smtp_address_equals
+ (new_rcpts[i].address, old_rcpts[j].address) )
+ break;
+ }
+
+ if ( j == old_count ) {
+ /* Not duplicate */
+ if ( del_len > 0 ) {
+ /* Perform pending deletion */
+ array_delete(&mtctx->uri->recipients, del_start, del_len);
+
+ /* Make sure the loop integrity is maintained */
+ i -= del_len;
+ new_rcpts = array_get(&mtctx->uri->recipients, &new_count);
+ }
+ del_len = 0;
+ } else {
+ /* Mark deletion */
+ if ( del_len == 0 )
+ del_start = i;
+ del_len++;
+ }
+ }
+
+ /* Perform pending deletion */
+ if ( del_len > 0 ) {
+ array_delete(&mtctx->uri->recipients, del_start, del_len);
+ }
+
+ return ( array_count(&mtctx->uri->recipients) > 0 ? 0 : 1 );
+}
+
+/*
+ * Action printing
+ */
+
+static void ntfy_mailto_action_print
+(const struct sieve_enotify_print_env *penv,
+ const struct sieve_enotify_action *nact)
+{
+ unsigned int count, i;
+ const struct uri_mailto_recipient *recipients;
+ const struct uri_mailto_header_field *headers;
+ struct ntfy_mailto_context *mtctx =
+ (struct ntfy_mailto_context *) nact->method_context;
+
+ /* Print main method parameters */
+
+ sieve_enotify_method_printf
+ (penv, " => importance : %llu\n",
+ (unsigned long long)nact->importance);
+
+ if ( nact->message != NULL )
+ sieve_enotify_method_printf
+ (penv, " => subject : %s\n", nact->message);
+ else if ( mtctx->uri->subject != NULL )
+ sieve_enotify_method_printf
+ (penv, " => subject : %s\n", mtctx->uri->subject);
+
+ if ( nact->from != NULL )
+ sieve_enotify_method_printf
+ (penv, " => from : %s\n", nact->from);
+
+ /* Print mailto: recipients */
+
+ sieve_enotify_method_printf(penv, " => recipients :\n" );
+
+ recipients = array_get(&mtctx->uri->recipients, &count);
+ if ( count == 0 ) {
+ sieve_enotify_method_printf(penv, " NONE, action has no effect\n");
+ } else {
+ for ( i = 0; i < count; i++ ) {
+ if ( recipients[i].carbon_copy )
+ sieve_enotify_method_printf
+ (penv, " + Cc: %s\n", recipients[i].full);
+ else
+ sieve_enotify_method_printf
+ (penv, " + To: %s\n", recipients[i].full);
+ }
+ }
+
+ /* Print accepted headers for notification message */
+
+ headers = array_get(&mtctx->uri->headers, &count);
+ if ( count > 0 ) {
+ sieve_enotify_method_printf(penv, " => headers :\n" );
+ for ( i = 0; i < count; i++ ) {
+ sieve_enotify_method_printf(penv, " + %s: %s\n",
+ headers[i].name, headers[i].body);
+ }
+ }
+
+ /* Print body for notification message */
+
+ if ( mtctx->uri->body != NULL )
+ sieve_enotify_method_printf
+ (penv, " => body : \n--\n%s\n--\n", mtctx->uri->body);
+
+ /* Finish output with an empty line */
+
+ sieve_enotify_method_printf(penv, "\n");
+}
+
+/*
+ * Action execution
+ */
+
+static bool _contains_8bit(const char *msg)
+{
+ const unsigned char *s = (const unsigned char *)msg;
+
+ for (; *s != '\0'; s++) {
+ if ((*s & 0x80) != 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int ntfy_mailto_send
+(const struct sieve_enotify_exec_env *nenv,
+ const struct sieve_enotify_action *nact,
+ const struct smtp_address *owner_email)
+{
+ struct sieve_instance *svinst = nenv->svinst;
+ const struct sieve_message_data *msgdata = nenv->msgdata;
+ const struct sieve_script_env *senv = nenv->scriptenv;
+ struct ntfy_mailto_context *mtctx =
+ (struct ntfy_mailto_context *) nact->method_context;
+ struct ntfy_mailto_config *mth_config =
+ (struct ntfy_mailto_config *)nenv->method->context;
+ struct sieve_address_source env_from =
+ mth_config->envelope_from;
+ const char *from = NULL;
+ const struct smtp_address *from_smtp = NULL;
+ const char *subject = mtctx->uri->subject;
+ const char *body = mtctx->uri->body;
+ string_t *to, *cc, *all;
+ const struct uri_mailto_recipient *recipients;
+ const struct uri_mailto_header_field *headers;
+ struct sieve_smtp_context *sctx;
+ struct ostream *output;
+ string_t *msg;
+ unsigned int count, i, hcount, h;
+ const char *outmsgid, *error;
+ int ret;
+
+ /* Get recipients */
+ recipients = array_get(&mtctx->uri->recipients, &count);
+ if ( count == 0 ) {
+ sieve_enotify_warning(nenv,
+ "notify mailto uri specifies no recipients; action has no effect");
+ return 0;
+ }
+
+ /* Just to be sure */
+ if ( !sieve_smtp_available(senv) ) {
+ sieve_enotify_global_warning(nenv,
+ "notify mailto method has no means to send mail");
+ return 0;
+ }
+
+ /* Determine which sender to use
+
+ From RFC 5436, Section 2.3:
+
+ The ":from" tag overrides the default sender of the notification
+ message. "Sender", here, refers to the value used in the [RFC5322]
+ "From" header. Implementations MAY also use this value in the
+ [RFC5321] "MAIL FROM" command (the "envelope sender"), or they may
+ prefer to establish a mailbox that receives bounces from notification
+ messages.
+ */
+ if ( (nenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 ) {
+ from_smtp = sieve_message_get_sender(nenv->msgctx);
+ if ( from_smtp == NULL ) {
+ /* "<>" */
+ i_zero(&env_from);
+ env_from.type = SIEVE_ADDRESS_SOURCE_EXPLICIT;
+ }
+ }
+ from = nact->from;
+ if ( (ret=sieve_address_source_get_address(&env_from, svinst,
+ senv, nenv->msgctx, nenv->flags, &from_smtp)) < 0 ) {
+ from_smtp = NULL;
+ } else if ( ret == 0 ) {
+ if ( mtctx->from_address != NULL )
+ from_smtp = mtctx->from_address;
+ else if ( svinst->user_email != NULL )
+ from_smtp = svinst->user_email;
+ else {
+ from_smtp = sieve_get_postmaster_smtp(senv);
+ if (from == NULL)
+ from = sieve_get_postmaster_address(senv);
+ }
+ }
+
+ /* Determine message from address */
+ if ( from == NULL ) {
+ if ( from_smtp == NULL )
+ from = sieve_get_postmaster_address(senv);
+ else {
+ from = t_strdup_printf("<%s>",
+ smtp_address_encode(from_smtp));
+ }
+ }
+
+ /* Determine subject */
+ if ( nact->message != NULL ) {
+ /* FIXME: handle UTF-8 */
+ subject = str_sanitize(nact->message, NTFY_MAILTO_MAX_SUBJECT);
+ } else if ( subject == NULL ) {
+ const char *const *hsubject;
+
+ /* Fetch subject from original message */
+ if ( mail_get_headers_utf8
+ (msgdata->mail, "subject", &hsubject) > 0 )
+ subject = str_sanitize(t_strdup_printf("Notification: %s", hsubject[0]),
+ NTFY_MAILTO_MAX_SUBJECT);
+ else
+ subject = "Notification: (no subject)";
+ }
+
+ /* Compose To and Cc headers */
+ to = NULL;
+ cc = NULL;
+ all = t_str_new(256);
+ for ( i = 0; i < count; i++ ) {
+ if ( recipients[i].carbon_copy ) {
+ if ( cc == NULL ) {
+ cc = t_str_new(256);
+ str_append(cc, recipients[i].full);
+ } else {
+ str_append(cc, ", ");
+ str_append(cc, recipients[i].full);
+ }
+ } else {
+ if ( to == NULL ) {
+ to = t_str_new(256);
+ str_append(to, recipients[i].full);
+ } else {
+ str_append(to, ", ");
+ str_append(to, recipients[i].full);
+ }
+ }
+ if ( i < 3) {
+ if ( i > 0 )
+ str_append(all, ", ");
+ str_append(all,
+ smtp_address_encode_path(recipients[i].address));
+ } else if (i == 3) {
+ str_printfa(all, ", ... (%u total)", count);
+ }
+ }
+
+ msg = t_str_new(512);
+ outmsgid = sieve_message_get_new_id(svinst);
+
+ rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION);
+ rfc2822_header_write(msg, "Message-ID", outmsgid);
+ rfc2822_header_write(msg, "Date", message_date_create(ioloop_time));
+ rfc2822_header_utf8_printf(msg, "Subject", "%s", subject);
+
+ rfc2822_header_write_address(msg, "From", from);
+
+ if ( to != NULL )
+ rfc2822_header_write_address(msg, "To", str_c(to));
+ if ( cc != NULL )
+ rfc2822_header_write_address(msg, "Cc", str_c(cc));
+
+ rfc2822_header_printf(msg, "Auto-Submitted",
+ "auto-notified; owner-email=\"%s\"",
+ smtp_address_encode(owner_email));
+ rfc2822_header_write(msg, "Precedence", "bulk");
+
+ /* Set importance */
+ switch ( nact->importance ) {
+ case 1:
+ rfc2822_header_write(msg, "X-Priority", "1 (Highest)");
+ rfc2822_header_write(msg, "Importance", "High");
+ break;
+ case 3:
+ rfc2822_header_write(msg, "X-Priority", "5 (Lowest)");
+ rfc2822_header_write(msg, "Importance", "Low");
+ break;
+ case 2:
+ default:
+ rfc2822_header_write(msg, "X-Priority", "3 (Normal)");
+ rfc2822_header_write(msg, "Importance", "Normal");
+ break;
+ }
+
+ /* Add custom headers */
+
+ headers = array_get(&mtctx->uri->headers, &hcount);
+ for ( h = 0; h < hcount; h++ ) {
+ const char *name = rfc2822_header_field_name_sanitize(headers[h].name);
+
+ rfc2822_header_write(msg, name, headers[h].body);
+ }
+
+ /* Generate message body */
+
+ rfc2822_header_write(msg, "MIME-Version", "1.0");
+ if ( body != NULL ) {
+ if (_contains_8bit(body)) {
+ rfc2822_header_write
+ (msg, "Content-Type", "text/plain; charset=utf-8");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit");
+ } else {
+ rfc2822_header_write
+ (msg, "Content-Type", "text/plain; charset=us-ascii");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit");
+ }
+ str_printfa(msg, "\r\n%s\r\n", body);
+
+ } else {
+ rfc2822_header_write
+ (msg, "Content-Type", "text/plain; charset=US-ASCII");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit");
+
+ str_append(msg, "\r\nNotification of new message.\r\n");
+ }
+
+ sctx = sieve_smtp_start(senv, from_smtp);
+
+ /* Send message to all recipients */
+ for ( i = 0; i < count; i++ )
+ sieve_smtp_add_rcpt(sctx, recipients[i].address);
+
+ output = sieve_smtp_send(sctx);
+ o_stream_nsend(output, str_data(msg), str_len(msg));
+
+ if ( (ret=sieve_smtp_finish(sctx, &error)) <= 0 ) {
+ if (ret < 0) {
+ sieve_enotify_global_error(nenv,
+ "failed to send mail notification to %s: %s (temporary failure)",
+ str_c(all), str_sanitize(error, 512));
+ } else {
+ sieve_enotify_global_log_error(nenv,
+ "failed to send mail notification to %s: %s (permanent failure)",
+ str_c(all), str_sanitize(error, 512));
+ }
+ } else {
+ struct event_passthrough *e =
+ sieve_enotify_create_finish_event(nenv)->
+ add_str("notify_target", str_c(all));
+
+ sieve_enotify_event_log(nenv, e->event(),
+ "sent mail notification to %s",
+ str_c(all));
+ }
+
+ return 0;
+}
+
+static int ntfy_mailto_action_execute
+(const struct sieve_enotify_exec_env *nenv,
+ const struct sieve_enotify_action *nact)
+{
+ struct sieve_instance *svinst = nenv->svinst;
+ const struct sieve_script_env *senv = nenv->scriptenv;
+ struct mail *mail = nenv->msgdata->mail;
+ const struct smtp_address *owner_email;
+ const char *const *hdsp;
+ int ret;
+
+ owner_email = svinst->user_email;
+ if ( owner_email == NULL &&
+ (nenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 )
+ owner_email = sieve_message_get_final_recipient(nenv->msgctx);
+ if ( owner_email == NULL ) {
+ owner_email = sieve_get_postmaster_smtp(senv);
+ }
+ i_assert( owner_email != NULL );
+
+ /* Is the message an automatic reply ? */
+ if ( (ret=mail_get_headers(mail, "auto-submitted", &hdsp)) < 0 ) {
+ sieve_enotify_critical(nenv,
+ "mailto notification: "
+ "failed to read `auto-submitted' header field",
+ "mailto notification: "
+ "failed to read `auto-submitted' header field: %s",
+ mailbox_get_last_internal_error(mail->box, NULL));
+ return -1;
+ }
+
+ /* Theoretically multiple headers could exist, so lets make sure */
+ if ( ret > 0 ) {
+ while ( *hdsp != NULL ) {
+ if ( strcasecmp(*hdsp, "no") != 0 ) {
+ const struct smtp_address *sender = NULL;
+ const char *from;
+
+ if ( (nenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 )
+ sender = sieve_message_get_sender(nenv->msgctx);
+ from = (sender == NULL ? "" : t_strdup_printf
+ (" from <%s>", smtp_address_encode(sender)));
+
+ sieve_enotify_global_info(nenv,
+ "not sending notification "
+ "for auto-submitted message%s", from);
+ return 0;
+ }
+ hdsp++;
+ }
+ }
+
+ T_BEGIN {
+ ret = ntfy_mailto_send(nenv, nact, owner_email);
+ } T_END;
+
+ return ret;
+}
+
+
+
+