src/deliver/mail-send.c
author Timo Sirainen <tss@iki.fi>
Thu Apr 16 22:39:18 2009 -0400 (2009-04-16)
branchHEAD
changeset 8246 311e3c9d395e
parent 8067 c89688a4e15f
child 8249 b217c085ba15
permissions -rw-r--r--
Released v1.1.14.
     1 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
     2 
     3 #include "lib.h"
     4 #include "ioloop.h"
     5 #include "hostpid.h"
     6 #include "istream.h"
     7 #include "str.h"
     8 #include "str-sanitize.h"
     9 #include "var-expand.h"
    10 #include "message-date.h"
    11 #include "message-size.h"
    12 #include "duplicate.h"
    13 #include "istream-header-filter.h"
    14 #include "smtp-client.h"
    15 #include "deliver.h"
    16 #include "mail-send.h"
    17 
    18 #include <stdlib.h>
    19 #include <sys/wait.h>
    20 
    21 int global_outgoing_count = 0;
    22 
    23 static const struct var_expand_table *
    24 get_var_expand_table(struct mail *mail, const char *reason,
    25 		     const char *recipient)
    26 {
    27 	static struct var_expand_table static_tab[] = {
    28 		{ 'n', NULL },
    29 		{ 'r', NULL },
    30 		{ 's', NULL },
    31 		{ 't', NULL },
    32 		{ '\0', NULL }
    33 	};
    34 	struct var_expand_table *tab;
    35 	const char *subject;
    36 
    37 	tab = t_malloc(sizeof(static_tab));
    38 	memcpy(tab, static_tab, sizeof(static_tab));
    39 
    40 	tab[0].value = "\r\n";
    41 	tab[1].value = reason;
    42 	if (mail_get_first_header(mail, "Subject", &subject) <= 0)
    43 		subject = "";
    44 	tab[2].value = str_sanitize(subject, 80);
    45 	tab[3].value = recipient;
    46 
    47 	return tab;
    48 }
    49 
    50 int mail_send_rejection(struct mail *mail, const char *recipient,
    51 			const char *reason)
    52 {
    53     struct istream *input;
    54     struct smtp_client *smtp_client;
    55     FILE *f;
    56     struct message_size hdr_size;
    57     const char *return_addr, *hdr;
    58     const unsigned char *data;
    59     const char *msgid, *orig_msgid, *boundary;
    60     string_t *str;
    61     size_t size;
    62     int ret;
    63 
    64     if (mail_get_first_header(mail, "Message-ID", &orig_msgid) < 0)
    65 	    orig_msgid = NULL;
    66     return_addr = deliver_get_return_address(mail);
    67     if (return_addr == NULL) {
    68 	    i_info("msgid=%s: Return-Path missing, rejection reason: %s",
    69 		   orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
    70 		   str_sanitize(reason, 512));
    71 	    return 0;
    72     }
    73 
    74     if (getenv("DEBUG") != NULL) {
    75 	    i_info("Sending a rejection to %s: %s", recipient,
    76 		   str_sanitize(reason, 512));
    77     }
    78 
    79     smtp_client = smtp_client_open(return_addr, NULL, &f);
    80 
    81     msgid = deliver_get_new_message_id();
    82     boundary = t_strdup_printf("%s/%s", my_pid, deliver_set->hostname);
    83 
    84     fprintf(f, "Message-ID: %s\r\n", msgid);
    85     fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time));
    86     fprintf(f, "From: Mail Delivery Subsystem <%s>\r\n",
    87 	    deliver_set->postmaster_address);
    88     fprintf(f, "To: <%s>\r\n", return_addr);
    89     fprintf(f, "MIME-Version: 1.0\r\n");
    90     fprintf(f, "Content-Type: "
    91 	    "multipart/report; report-type=disposition-notification;\r\n"
    92 	    "\tboundary=\"%s\"\r\n", boundary);
    93 
    94     str = t_str_new(256);
    95     var_expand(str, deliver_set->rejection_subject,
    96 	       get_var_expand_table(mail, reason, recipient));
    97     fprintf(f, "Subject: %s\r\n", str_c(str));
    98 
    99     fprintf(f, "Auto-Submitted: auto-replied (rejected)\r\n");
   100     fprintf(f, "Precedence: bulk\r\n");
   101     fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n");
   102 
   103     /* human readable status report */
   104     fprintf(f, "--%s\r\n", boundary);
   105     fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n");
   106     fprintf(f, "Content-Disposition: inline\r\n");
   107     fprintf(f, "Content-Transfer-Encoding: 8bit\r\n\r\n");
   108 
   109     str_truncate(str, 0);
   110     var_expand(str, deliver_set->rejection_reason,
   111 	       get_var_expand_table(mail, reason, recipient));
   112     fprintf(f, "%s\r\n", str_c(str));
   113 
   114     /* MDN status report */
   115     fprintf(f, "--%s\r\n"
   116 	    "Content-Type: message/disposition-notification\r\n\r\n",
   117 	    boundary);
   118     fprintf(f, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n",
   119 	    deliver_set->hostname);
   120     if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0)
   121 	    fprintf(f, "Original-Recipient: rfc822; %s\r\n", hdr);
   122     fprintf(f, "Final-Recipient: rfc822; %s\r\n", recipient);
   123 
   124     if (orig_msgid != NULL)
   125 	fprintf(f, "Original-Message-ID: %s\r\n", orig_msgid);
   126     fprintf(f, "Disposition: "
   127 	    "automatic-action/MDN-sent-automatically; deleted\r\n");
   128     fprintf(f, "\r\n");
   129 
   130     /* original message's headers */
   131     fprintf(f, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary);
   132 
   133     if (mail_get_stream(mail, &hdr_size, NULL, &input) == 0) {
   134 	    /* Note: If you add more headers, they need to be sorted.
   135 	       We'll drop Content-Type because we're not including the message
   136 	       body, and having a multipart Content-Type may confuse some
   137 	       MIME parsers when they don't see the message boundaries. */
   138 	    static const char *const exclude_headers[] = {
   139 		    "Content-Type"
   140 	    };
   141 
   142 	    input = i_stream_create_header_filter(input,
   143 	    		HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR |
   144 			HEADER_FILTER_HIDE_BODY, exclude_headers,
   145 			N_ELEMENTS(exclude_headers),
   146 			null_header_filter_callback, NULL);
   147 
   148 	    while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
   149 		    if (fwrite(data, size, 1, f) == 0)
   150 			    break;
   151 		    i_stream_skip(input, size);
   152 	    }
   153 	    i_stream_unref(&input);
   154 
   155 	    i_assert(ret != 0);
   156     }
   157 
   158     fprintf(f, "\r\n\r\n--%s--\r\n", boundary);
   159     return smtp_client_close(smtp_client);
   160 }
   161 
   162 int mail_send_forward(struct mail *mail, const char *forwardto)
   163 {
   164     static const char *hide_headers[] = {
   165         "Return-Path"
   166     };
   167     struct istream *input;
   168     struct smtp_client *smtp_client;
   169     FILE *f;
   170     const unsigned char *data;
   171     const char *return_path;
   172     size_t size;
   173     int ret;
   174 
   175     if (mail_get_stream(mail, NULL, NULL, &input) < 0)
   176 	    return -1;
   177 
   178     if (mail_get_first_header(mail, "Return-Path", &return_path) <= 0)
   179 	    return_path = "";
   180 
   181     if (getenv("DEBUG") != NULL) {
   182 	    i_info("Sending a forward to <%s> with return path <%s>",
   183 		   forwardto, return_path);
   184     }
   185 
   186     smtp_client = smtp_client_open(forwardto, return_path, &f);
   187 
   188     input = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE |
   189                                           HEADER_FILTER_NO_CR, hide_headers,
   190                                           N_ELEMENTS(hide_headers),
   191 					  null_header_filter_callback, NULL);
   192 
   193     while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
   194 	    if (fwrite(data, size, 1, f) == 0)
   195 		    break;
   196 	    i_stream_skip(input, size);
   197     }
   198     i_stream_unref(&input);
   199 
   200     return smtp_client_close(smtp_client);
   201 }
   202