mail-send.c revision 3e5a1673793631ea5cf0371e9ef5360b5e525335
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "lib.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "ioloop.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "hostpid.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "istream.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "ostream.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "str.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "str-sanitize.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "var-expand.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "message-date.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "message-size.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "istream-header-filter.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "mail-storage.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "mail-storage-settings.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "lda-settings.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "mail-deliver.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "smtp-submit.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "mail-send.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include <sys/wait.h>
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic const struct var_expand_table *
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomiget_var_expand_table(struct mail *mail, const char *reason,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *recipient)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *subject;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (mail_get_first_header(mail, "Subject", &subject) <= 0)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi subject = "";
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const struct var_expand_table stack_tab[] = {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi { 'n', "\r\n", "crlf" },
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi { 'r', reason, "reason" },
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi { 's', str_sanitize(subject, 80), "subject" },
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi { 't', recipient, "to" },
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi { '\0', NULL, NULL }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi };
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct var_expand_table *tab;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi tab = t_malloc_no0(sizeof(stack_tab));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi memcpy(tab, stack_tab, sizeof(stack_tab));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return tab;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomiint mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *reason)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct mail *mail = ctx->src_mail;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct istream *input;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct smtp_client *smtp_client;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct ostream *output;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *return_addr, *hdr;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *value, *msgid, *orig_msgid, *boundary, *error;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const struct var_expand_table *vtable;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi string_t *str;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi int ret;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (mail_get_first_header(mail, "Message-ID", &orig_msgid) < 0)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi orig_msgid = NULL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (mail_get_first_header(mail, "Auto-Submitted", &value) > 0 &&
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi strcasecmp(value, "no") != 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_info("msgid=%s: Auto-submitted message discarded: %s",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_sanitize(reason, 512));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return_addr = mail_deliver_get_return_address(ctx);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (return_addr == NULL) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_info("msgid=%s: Return-Path missing, rejection reason: %s",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_sanitize(reason, 512));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (mailbox_get_settings(mail->box)->mail_debug) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_debug("Sending a rejection to %s: %s",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return_addr, str_sanitize(reason, 512));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi vtable = get_var_expand_table(mail, reason, recipient);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi smtp_client = smtp_client_init(ctx->set, NULL);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi smtp_client_add_rcpt(smtp_client, return_addr);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi output = smtp_client_send(smtp_client);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi msgid = mail_deliver_get_new_message_id(ctx);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi boundary = t_strdup_printf("%s/%s", my_pid, ctx->set->hostname);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str = t_str_new(512);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Message-ID: %s\r\n", msgid);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Date: %s\r\n", message_date_create(ioloop_time));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "From: Mail Delivery Subsystem <%s>\r\n",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ctx->set->postmaster_address);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "To: <%s>\r\n", return_addr);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "MIME-Version: 1.0\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Content-Type: "
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "multipart/report; report-type=%s;\r\n"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "\tboundary=\"%s\"\r\n",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ctx->dsn ? "delivery-status" : "disposition-notification",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi boundary);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "Subject: ");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (var_expand(str, ctx->set->rejection_subject,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi vtable, &error) <= 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_error("Failed to expand rejection_subject=%s: %s",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ctx->set->rejection_subject, error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "Auto-Submitted: auto-replied (rejected)\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "Precedence: bulk\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "\r\nThis is a MIME-encapsulated message\r\n\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* human readable status report */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "--%s\r\n", boundary);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "Content-Type: text/plain; charset=utf-8\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "Content-Disposition: inline\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "Content-Transfer-Encoding: 8bit\r\n\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (var_expand(str, ctx->set->rejection_reason,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi vtable, &error) <= 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_error("Failed to expand rejection_reason=%s: %s",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ctx->set->rejection_reason, error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ctx->dsn) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* DSN status report: For LDA rejects. currently only used when
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi user is out of quota */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "--%s\r\n"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "Content-Type: message/delivery-status\r\n\r\n",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi boundary);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Reporting-MTA: dns; %s\r\n", ctx->set->hostname);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "Action: failed\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Status: %s\r\n", ctx->mailbox_full ? "5.2.2" : "5.2.0");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* MDN status report: For Sieve "reject" */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "--%s\r\n"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "Content-Type: message/disposition-notification\r\n\r\n",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi boundary);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ctx->set->hostname);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (orig_msgid != NULL)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "Original-Message-ID: %s\r\n", orig_msgid);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "Disposition: "
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "automatic-action/MDN-sent-automatically; deleted\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_append(str, "\r\n");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* original message's headers */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi o_stream_nsend(output, str_data(str), str_len(str));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (mail_get_hdr_stream(mail, NULL, &input) == 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* Note: If you add more headers, they need to be sorted.
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi We'll drop Content-Type because we're not including the message
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi body, and having a multipart Content-Type may confuse some
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi MIME parsers when they don't see the message boundaries. */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi static const char *const exclude_headers[] = {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "Content-Type"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi };
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi input = i_stream_create_header_filter(input,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR |
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi HEADER_FILTER_HIDE_BODY, exclude_headers,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi N_ELEMENTS(exclude_headers),
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi *null_header_filter_callback, (void *)NULL);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi o_stream_nsend_istream(output, input);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_stream_unref(&input);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_truncate(str, 0);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_printfa(str, "\r\n\r\n--%s--\r\n", boundary);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi o_stream_nsend(output, str_data(str), str_len(str));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((ret = smtp_client_deinit_timeout
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi (smtp_client, ctx->timeout_secs, &error)) < 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_error("msgid=%s: Temporarily failed to send rejection: %s",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_sanitize(error, 512));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else if (ret == 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_info("msgid=%s: Permanently failed to send rejection: %s",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi str_sanitize(error, 512));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return ret < 0 ? -1 : 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi