int mail_send_forward(struct mail_deliver_context *ctx, const char *forwardto) { static const char *hide_headers[] = { "Return-Path" }; struct istream *input; struct ostream *output; struct smtp_client *smtp_client; const char *return_path; if (mail_get_stream(ctx->src_mail, NULL, NULL, &input) < 0) return -1; return_path = mail_deliver_get_return_address(ctx); if (mailbox_get_settings(ctx->src_mail->box)->mail_debug) { i_debug("Sending a forward to <%s> with return path <%s>", forwardto, return_path); } smtp_client = smtp_client_open(ctx->set, forwardto, return_path, &output); input = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hide_headers, N_ELEMENTS(hide_headers), *null_header_filter_callback, (void *)NULL); (void)o_stream_send_istream(output, input); i_stream_unref(&input); return smtp_client_close(smtp_client); }
int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient, const char *reason) { struct mail *mail = ctx->src_mail; struct istream *input; struct smtp_client *smtp_client; struct ostream *output; const char *return_addr, *hdr; const char *value, *msgid, *orig_msgid, *boundary; string_t *str; int ret; if (mail_get_first_header(mail, "Message-ID", &orig_msgid) < 0) orig_msgid = NULL; if (mail_get_first_header(mail, "Auto-Submitted", &value) > 0 && strcasecmp(value, "no") != 0) { i_info("msgid=%s: Auto-submitted message discarded: %s", orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), str_sanitize(reason, 512)); return 0; } return_addr = mail_deliver_get_return_address(ctx); if (return_addr == NULL) { i_info("msgid=%s: Return-Path missing, rejection reason: %s", orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), str_sanitize(reason, 512)); return 0; } if (mailbox_get_settings(mail->box)->mail_debug) { i_debug("Sending a rejection to %s: %s", recipient, str_sanitize(reason, 512)); } smtp_client = smtp_client_open(ctx->set, return_addr, NULL, &output); msgid = mail_deliver_get_new_message_id(ctx); boundary = t_strdup_printf("%s/%s", my_pid, ctx->set->hostname); str = t_str_new(512); str_printfa(str, "Message-ID: %s\r\n", msgid); str_printfa(str, "Date: %s\r\n", message_date_create(ioloop_time)); str_printfa(str, "From: Mail Delivery Subsystem <%s>\r\n", ctx->set->postmaster_address); str_printfa(str, "To: <%s>\r\n", return_addr); str_append(str, "MIME-Version: 1.0\r\n"); str_printfa(str, "Content-Type: " "multipart/report; report-type=%s;\r\n" "\tboundary=\"%s\"\r\n", ctx->dsn ? "delivery-status" : "disposition-notification", boundary); str_append(str, "Subject: "); var_expand(str, ctx->set->rejection_subject, get_var_expand_table(mail, reason, recipient)); str_append(str, "\r\n"); str_append(str, "Auto-Submitted: auto-replied (rejected)\r\n"); str_append(str, "Precedence: bulk\r\n"); str_append(str, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); /* human readable status report */ str_printfa(str, "--%s\r\n", boundary); str_append(str, "Content-Type: text/plain; charset=utf-8\r\n"); str_append(str, "Content-Disposition: inline\r\n"); str_append(str, "Content-Transfer-Encoding: 8bit\r\n\r\n"); var_expand(str, ctx->set->rejection_reason, get_var_expand_table(mail, reason, recipient)); str_append(str, "\r\n"); if (ctx->dsn) { /* DSN status report: For LDA rejects. currently only used when user is out of quota */ str_printfa(str, "--%s\r\n" "Content-Type: message/delivery-status\r\n\r\n", boundary); str_printfa(str, "Reporting-MTA: dns; %s\r\n", ctx->set->hostname); if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0) str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr); str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient); str_append(str, "Action: failed\r\n"); str_printfa(str, "Status: %s\r\n", ctx->mailbox_full ? "5.2.2" : "5.2.0"); } else { /* MDN status report: For Sieve "reject" */ str_printfa(str, "--%s\r\n" "Content-Type: message/disposition-notification\r\n\r\n", boundary); str_printfa(str, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n", ctx->set->hostname); if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0) str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr); str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient); if (orig_msgid != NULL) str_printfa(str, "Original-Message-ID: %s\r\n", orig_msgid); str_append(str, "Disposition: " "automatic-action/MDN-sent-automatically; deleted\r\n"); } str_append(str, "\r\n"); /* original message's headers */ str_printfa(str, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary); o_stream_nsend(output, str_data(str), str_len(str)); if (mail_get_hdr_stream(mail, 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, (void *)NULL); ret = o_stream_send_istream(output, input); i_stream_unref(&input); i_assert(ret != 0); } str_truncate(str, 0); str_printfa(str, "\r\n\r\n--%s--\r\n", boundary); o_stream_nsend(output, str_data(str), str_len(str)); return smtp_client_close(smtp_client); }
/** * @brief Send (relay) a message composed by a user via a portal session. * @see smtp_relay_message() - a lot of logic borrowed from here. * @param from a pointer to a managed string containing the email address specified as the From address. * @param to a pointer to a managed string containing the destination email address of the message. * @param data a pointer to a managed string containing the raw data of the mail message. * @param send_size if greater than 0, specify the optional SIZE parameter to the MAIL FROM command. * @param errmsg the address of a pointer to a null-terminated string that will be set to a descriptive error message on failure. * @return true if the mail message was sent successfully, or false otherwise. */ bool_t portal_smtp_relay_message(stringer_t *from, inx_t *to, stringer_t *data, size_t send_size, chr_t **errmsg) { inx_cursor_t *cursor; stringer_t *to_address; client_t *client; int_t state, nsentto = 0; if (!from || !to || !data || !errmsg) { *errmsg = "Unexpected internal failure occurred while sending message."; return false; } /*if (mail_add_outbound_headers(con) != 1) { log_pedantic("Could not add the outbound headers."); return false; }*/ // Open the connection to the SMTP server. if (!(client = smtp_client_connect(0))) { *errmsg = "Encountered transport error with relay server."; return false; } // Send HELO. if (smtp_client_send_helo(client)!= 1) { *errmsg = "Handshake with relay server failed."; smtp_client_close(client); return false; } // Send MAIL FROM. if ((state = smtp_client_send_mailfrom(client, from, send_size)) != 1) { *errmsg = "Error occurred while sending From field to email server."; smtp_client_close(client); return false; } // Send the RCPT TO command. if (!(cursor = inx_cursor_alloc(to))) { *errmsg = "Internal occurred while expanding recipient list."; smtp_client_close(client); return false; } while ((to_address = inx_cursor_value_next(cursor))) { if ((state = smtp_client_send_rcptto(client, to_address)) != 1) { *errmsg = "Error occurred while specifying recipient to email server."; smtp_client_close(client); inx_cursor_free(cursor); return false; } nsentto++; } inx_cursor_free(cursor); if (!nsentto) { *errmsg = "Mail message could not be sent without recipient."; smtp_client_close(client); return false; } // Send the the message. if ((state = smtp_client_send_data(client, data)) != 1) { *errmsg = "Error occurred while sending email message body."; smtp_client_close(client); return false; } // Store the result. //*result = st_dupe_opts(MANAGED_T | CONTIGUOUS | HEAP, &(client->line)); smtp_client_close(client); // TODO: smtp_update_transmission_stats() needs to be called here. return true; }