X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fdeliver.c;h=b0b4601dc3575df6188bec955dfc2f9a93999b2c;hb=96e47838f9aaea7f89685163bcb01164f4444378;hp=7af101ac0587120ba4335a66c3c07cdf18642ebc;hpb=2e6afa4f11972312d3dbb9bb1d4f4bf585a3cdd2;p=exim.git diff --git a/src/src/deliver.c b/src/src/deliver.c index 7af101ac0..b0b4601dc 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -63,6 +63,10 @@ static address_item *addr_new = NULL; static address_item *addr_remote = NULL; static address_item *addr_route = NULL; static address_item *addr_succeed = NULL; +#ifdef EXPERIMENTAL_DSN +static address_item *addr_dsntmp = NULL; +static address_item *addr_senddsn = NULL; +#endif static FILE *message_log = NULL; static BOOL update_spool; @@ -838,17 +842,17 @@ else /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */ -if ((log_extra_selector & LX_smtp_confirmation) != 0 && - addr->message != NULL && - ((addr->host_used != NULL) || (Ustrcmp(addr->transport->driver_name, "lmtp") == 0))) +if (log_extra_selector & LX_smtp_confirmation && + addr->message && + (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0)) { int i; uschar *p = big_buffer; uschar *ss = addr->message; *p++ = '\"'; - for (i = 0; i < 100 && ss[i] != 0; i++) + for (i = 0; i < 256 && ss[i] != 0; i++) /* limit logged amount */ { - if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; + if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */ *p++ = ss[i]; } *p++ = '\"'; @@ -859,16 +863,12 @@ if ((log_extra_selector & LX_smtp_confirmation) != 0 && /* Time on queue and actual time taken to deliver */ if ((log_extra_selector & LX_queue_time) != 0) - { s = string_append(s, &size, &ptr, 2, US" QT=", - readconf_printtime(time(NULL) - received_time)); - } + readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) ); if ((log_extra_selector & LX_deliver_time) != 0) - { s = string_append(s, &size, &ptr, 2, US" DT=", readconf_printtime(addr->more_errno)); - } /* string_cat() always leaves room for the terminator. Release the store we used to build the line after writing it. */ @@ -3015,7 +3015,7 @@ while (!done) (void) tls_import_cert(ptr, &addr->ourcert); break; - #ifdef EXPERIMENTAL_OCSP + #ifndef DISABLE_OCSP case '4': addr->ocsp = OCSP_NOT_REQ; if (*ptr) @@ -3049,6 +3049,15 @@ while (!done) break; #endif + #ifdef EXPERIMENTAL_DSN + case 'D': + if (addr == NULL) break; + memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware)); + ptr += sizeof(addr->dsn_aware); + DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware); + break; + #endif + case 'A': if (addr == NULL) { @@ -4154,7 +4163,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) *ptr++ = 0; rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); } - # ifdef EXPERIMENTAL_OCSP + #ifndef DISABLE_OCSP if (addr->ocsp > OCSP_NOT_REQ) { ptr = big_buffer; @@ -4192,6 +4201,13 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) rmt_dlv_checked_write(fd, "P", 1); #endif + #ifdef EXPERIMENTAL_DSN + big_buffer[0] = 'D'; + memcpy(big_buffer+1, &addr->dsn_aware, sizeof(addr->dsn_aware)); + rmt_dlv_checked_write(fd, big_buffer, sizeof(addr->dsn_aware) + 1); + DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware); + #endif + /* Retry information: for most success cases this will be null. */ for (r = addr->retries; r != NULL; r = r->next) @@ -5342,6 +5358,14 @@ if (process_recipients != RECIP_IGNORE) if (r->pno >= 0) new->onetime_parent = recipients_list[r->pno].address; + #ifdef EXPERIMENTAL_DSN + /* If DSN support is enabled, set the dsn flags and the original receipt + to be passed on to other DSN enabled MTAs */ + new->dsn_flags = r->dsn_flags & rf_dsnflags; + new->dsn_orcpt = r->orcpt; + DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", new->dsn_orcpt, new->dsn_flags); + #endif + switch (process_recipients) { /* RECIP_DEFER is set when a system filter freezes a message. */ @@ -6286,6 +6310,12 @@ if (addr_remote != NULL) regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); #endif + #ifdef EXPERIMENTAL_DSN + /* Set the regex to check for DSN support on remote MTA */ + if (regex_DSN == NULL) regex_DSN = + regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE); + #endif + /* Now sort the addresses if required, and do the deliveries. The yield of do_remote_deliveries is FALSE when mua_wrapper is set and all addresses cannot be delivered in one transaction. */ @@ -6390,6 +6420,169 @@ prevents actual delivery. */ else if (!dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed); +#ifdef EXPERIMENTAL_DSN +/* Send DSN for successful messages */ +addr_dsntmp = addr_succeed; +addr_senddsn = NULL; + +while(addr_dsntmp != NULL) + { + DEBUG(D_deliver) + debug_printf("DSN: processing router : %s\n", addr_dsntmp->router->name); + + DEBUG(D_deliver) + debug_printf("DSN: processing successful delivery address: %s\n", addr_dsntmp->address); + + /* af_ignore_error not honored here. it's not an error */ + + DEBUG(D_deliver) debug_printf("DSN: Sender_address: %s\n", sender_address); + DEBUG(D_deliver) debug_printf("DSN: orcpt: %s flags: %d\n", addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags); + DEBUG(D_deliver) debug_printf("DSN: envid: %s ret: %d\n", dsn_envid, dsn_ret); + DEBUG(D_deliver) debug_printf("DSN: Final recipient: %s\n", addr_dsntmp->address); + DEBUG(D_deliver) debug_printf("DSN: Remote SMTP server supports DSN: %d\n", addr_dsntmp->dsn_aware); + + /* send report if next hop not DSN aware or a router flagged "last DSN hop" + and a report was requested */ + if (((addr_dsntmp->dsn_aware != dsn_support_yes) || + ((addr_dsntmp->dsn_flags & rf_dsnlasthop) != 0)) + && + (((addr_dsntmp->dsn_flags & rf_dsnflags) != 0) && + ((addr_dsntmp->dsn_flags & rf_notify_success) != 0))) + { + /* copy and relink address_item and send report with all of them at once later */ + address_item *addr_next; + addr_next = addr_senddsn; + addr_senddsn = store_get(sizeof(address_item)); + memcpy(addr_senddsn, addr_dsntmp, sizeof(address_item)); + addr_senddsn->next = addr_next; + } + else + { + DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message ***\n"); + } + + addr_dsntmp = addr_dsntmp->next; + } + +if (addr_senddsn != NULL) + { + pid_t pid; + int fd; + + /* create exim process to send message */ + pid = child_open_exim(&fd); + + DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid); + + if (pid < 0) /* Creation of child failed */ + { + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " + "create child process to send failure message: %s", getpid(), + getppid(), strerror(errno)); + + DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); + + } + else /* Creation of child succeeded */ + { + FILE *f = fdopen(fd, "wb"); + /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ + int topt = topt_add_return_path | topt_no_body; + uschar boundaryStr[64]; + + DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address); + + /* build unique id for MIME boundary */ + snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d", + time(NULL), rand()); + DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", boundaryStr); + + if (errors_reply_to) + fprintf(f, "Reply-To: %s\n", errors_reply_to); + + fprintf(f, "Auto-Submitted: auto-generated\n" + "From: Mail Delivery System \n" + "To: %s\n" + "Subject: Delivery Status Notification\n" + "Content-Type: multipart/report; report-type=delivery-status; boundary=%s\n" + "MIME-Version: 1.0\n\n" + + "--%s\n" + "Content-type: text/plain; charset=us-ascii\n\n" + + "This message was created automatically by mail delivery software.\n" + " ----- The following addresses had successful delivery notifications -----\n" + qualify_domain_sender, sender_addres, boundaryStrs, boundarySt); + + addr_dsntmp = addr_senddsn; + while(addr_dsntmp) + { + fprintf(f, "<%s> (relayed %s)\n\n", + addr_dsntmp->address, + (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 + ? "via non DSN router" + : addr_dsntmp->dsn_aware == dsn_support_no + ? "to non-DSN-aware mailer" + : "via non \"Remote SMTP\" router" + ); + addr_dsntmp = addr_dsntmp->next; + } + fprintf(f, "--%s\n" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + boundaryStr, smtp_active_hostname); + + if (dsn_envid != NULL) { + /* must be decoded from xtext: see RFC 3461:6.3a */ + uschar *xdec_envid; + if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) + fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); + else + fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); + } + fputc('\n', f); + + for (addr_dsntmp = addr_senddsn; + addr_dsntmp; + addr_dsntmp = addr_dsntmp->next) + { + if (addr_dsntmp->dsn_orcpt) + fprintf(f,"Original-Recipient: %s\n", addr_dsntmp->dsn_orcpt); + + fprintf(f, "Action: delivered\n" + "Final-Recipient: rfc822;%s\n" + "Status: 2.0.0\n", + addr_dsntmp->address); + + if (addr_dsntmp->host_used && addr_dsntmp->host_used->name) + fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n", + addr_dsntmp->host_used->name); + else + fprintf(f,"Diagnostic-Code: X-Exim; relayed via non %s router\n", + (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP"); + fputc('\n', f); + } + + fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", boundaryStr); + + fflush(f); + transport_filter_argv = NULL; /* Just in case */ + return_path = sender_address; /* In case not previously set */ + + /* Write the original email out */ + transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); + fflush(f); + + fprintf(f,"\n"); + fprintf(f,"--%s--\n", boundaryStr); + + fflush(f); + fclose(f); + rc = child_close(pid, 0); /* Waits for child to close, no timeout */ + } + } +#endif /*EXPERIMENTAL_DSN*/ + /* If any addresses failed, we must send a message to somebody, unless af_ignore_error is set, in which case no action is taken. It is possible for several messages to get sent if there are addresses with different @@ -6447,8 +6640,13 @@ while (addr_failed != NULL) it from the list, throw away any saved message file, log it, and mark the recipient done. */ - if (testflag(addr_failed, af_ignore_error)) - { + if (testflag(addr_failed, af_ignore_error) +#ifdef EXPERIMENTAL_DSN + || (((addr_failed->dsn_flags & rf_dsnflags) != 0) + && ((addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure)) +#endif + ) + { addr = addr_failed; addr_failed = addr->next; if (addr->return_filename != NULL) Uunlink(addr->return_filename); @@ -6500,6 +6698,12 @@ while (addr_failed != NULL) BOOL to_sender = strcmpic(sender_address, bounce_recipient) == 0; int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) * DELIVER_IN_BUFFER_SIZE; +#ifdef EXPERIMENTAL_DSN + uschar boundaryStr[64]; + uschar *dsnlimitmsg; + uschar *dsnnotifyhdr; + int topt; +#endif DEBUG(D_deliver) debug_printf("sending error message to: %s\n", bounce_recipient); @@ -6553,57 +6757,70 @@ while (addr_failed != NULL) moan_write_from(f); fprintf(f, "To: %s\n", bounce_recipient); +#ifdef EXPERIMENTAL_DSN + /* generate boundary string and output MIME-Headers */ + snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d", + time(NULL), rand()); + + fprintf(f, "Content-Type: multipart/report;" + " report-type=delivery-status; boundary=%s\n" + "MIME-Version: 1.0\n", + boundaryStr); +#endif + /* Open a template file if one is provided. Log failure to open, but carry on - default texts will be used. */ - if (bounce_message_file != NULL) - { - emf = Ufopen(bounce_message_file, "rb"); - if (emf == NULL) + if (bounce_message_file) + if (!(emf = Ufopen(bounce_message_file, "rb"))) log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for error " "message texts: %s", bounce_message_file, strerror(errno)); - } /* Quietly copy to configured additional addresses if required. */ - bcc = moan_check_errorcopy(bounce_recipient); - if (bcc != NULL) fprintf(f, "Bcc: %s\n", bcc); + if ((bcc = moan_check_errorcopy(bounce_recipient))) + fprintf(f, "Bcc: %s\n", bcc); /* The texts for the message can be read from a template file; if there isn't one, or if it is too short, built-in texts are used. The first emf text is a Subject: and any other headers. */ - emf_text = next_emf(emf, US"header"); - if (emf_text != NULL) fprintf(f, "%s\n", emf_text); else - { + if ((emf_text = next_emf(emf, US"header"))) + fprintf(f, "%s\n", emf_text); + else fprintf(f, "Subject: Mail delivery failed%s\n\n", to_sender? ": returning message to sender" : ""); - } - emf_text = next_emf(emf, US"intro"); - if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else +#ifdef EXPERIMENTAL_DSN + /* output human readable part as text/plain section */ + fprintf(f, "--%s\n" + "Content-type: text/plain; charset=us-ascii\n\n", + boundaryStr); +#endif + + if ((emf_text = next_emf(emf, US"intro"))) + fprintf(f, "%s", CS emf_text); + else { fprintf(f, /* This message has been reworded several times. It seems to be confusing to somebody, however it is worded. I have retreated to the original, simple wording. */ "This message was created automatically by mail delivery software.\n"); - if (bounce_message_text != NULL) fprintf(f, "%s", CS bounce_message_text); + + if (bounce_message_text) + fprintf(f, "%s", CS bounce_message_text); if (to_sender) - { fprintf(f, "\nA message that you sent could not be delivered to one or more of its\n" "recipients. This is a permanent error. The following address(es) failed:\n"); - } else - { fprintf(f, "\nA message sent by\n\n <%s>\n\n" "could not be delivered to one or more of its recipients. The following\n" "address(es) failed:\n", sender_address); - } } - fprintf(f, "\n"); + fputc('\n', f); /* Process the addresses, leaving them on the msgchain if they have a file name for a return message. (There has already been a check in @@ -6640,7 +6857,7 @@ wording. */ } } - fprintf(f, "\n"); + fputc('\n', f); /* Get the next text, whether we need it or not, so as to be positioned for the one after. */ @@ -6654,11 +6871,13 @@ wording. */ fd, and the return_filename field in the *last* one will be set (to the name of the file). */ - if (msgchain != NULL) + if (msgchain) { address_item *nextaddr; - if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else + if (emf_text) + fprintf(f, "%s", CS emf_text); + else fprintf(f, "The following text was generated during the delivery " "attempt%s:\n", (filecount > 1)? "s" : ""); @@ -6670,15 +6889,15 @@ wording. */ /* List all the addresses that relate to this file */ - fprintf(f, "\n"); - while(addr != NULL) /* Insurance */ + fputc('\n', f); + while(addr) /* Insurance */ { print_address_information(addr, f, US"------ ", US"\n ", US" ------\n"); - if (addr->return_filename != NULL) break; + if (addr->return_filename) break; addr = addr->next; } - fprintf(f, "\n"); + fputc('\n', f); /* Now copy the file */ @@ -6701,8 +6920,37 @@ wording. */ addr->next = handled_addr; handled_addr = topaddr; } - fprintf(f, "\n"); + fputc('\n', f); + } + +#ifdef EXPERIMENTAL_DSN + /* output machine readable part */ + fprintf(f, "--%s\n" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + boundaryStr, smtp_active_hostname); + + if (dsn_envid) + { + /* must be decoded from xtext: see RFC 3461:6.3a */ + uschar *xdec_envid; + if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) + fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); + else + fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); + } + fputc('\n', f); + + for (addr = handled_addr; addr; addr = addr->next) + { + fprintf(f, "Action: failed\n" + "Final-Recipient: rfc822;%s\n", addr->address + "Status: 5.0.0\n"); + if (addr->host_used && addr->host_used->name) + fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n", + addr->host_used->name, addr->basic_errno); } +#endif /* Now copy the message, trying to give an intelligible comment if it is too long for it all to be copied. The limit isn't strictly @@ -6711,12 +6959,15 @@ wording. */ emf_text = next_emf(emf, US"copy"); +#ifndef EXPERIMENTAL_DSN if (bounce_return_message) { int topt = topt_add_return_path; if (!bounce_return_body) topt |= topt_no_body; - if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else + if (emf_text) + fprintf(f, "%s", CS emf_text); + else { if (bounce_return_body) fprintf(f, "------ This is a copy of the message, including all the headers. ------\n"); @@ -6739,18 +6990,17 @@ wording. */ { struct stat statbuf; if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max) - { - if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else - { + if (emf_text) + fprintf(f, "%s", CS emf_text); + else fprintf(f, "------ The body of the message is " OFF_T_FMT " characters long; only the first\n" "------ %d or so are included here.\n", statbuf.st_size, max); - } - } } - fprintf(f, "\n"); + fputc('\n', f); fflush(f); + transport_filter_argv = NULL; /* Just in case */ return_path = sender_address; /* In case not previously set */ transport_write_message(NULL, fileno(f), topt, @@ -6759,12 +7009,71 @@ wording. */ /* Write final text and close the template file if one is open */ - if (emf != NULL) + if (emf) { - emf_text = next_emf(emf, US"final"); - if (emf_text != NULL) fprintf(f, "%s", CS emf_text); + if ((emf_text = next_emf(emf, US"final"))) + fprintf(f, "%s", CS emf_text); (void)fclose(emf); } +#else + /* add message body + we ignore the intro text from template and add + the text for bounce_return_size_limit at the end. + + bounce_return_message is ignored + in case RET= is defined we honor these values + otherwise bounce_return_body is honored. + + bounce_return_size_limit is always honored. + */ + + fprintf(f, "\n--%s\n", boundaryStr); + + dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned"; + dsnnotifyhdr = NULL; + topt = topt_add_return_path; + + /* RET=HDRS? top priority */ + if (dsn_ret == dsn_ret_hdrs) + topt |= topt_no_body; + else + /* no full body return at all? */ + if (!bounce_return_body) + { + topt |= topt_no_body; + /* add header if we overrule RET=FULL */ + if (dsn_ret == dsn_ret_full) + dsnnotifyhdr = dsnlimitmsg; + } + /* size limited ... return headers only if limit reached */ + else if (bounce_return_size_limit > 0) + { + struct stat statbuf; + if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max) + { + topt |= topt_no_body; + dsnnotifyhdr = dsnlimitmsg; + } + } + + if (topt & topt_no_body) + fprintf(f,"Content-type: text/rfc822-headers\n\n"); + else + fprintf(f,"Content-type: message/rfc822\n\n"); + + fflush(f); + transport_filter_argv = NULL; /* Just in case */ + return_path = sender_address; /* In case not previously set */ + transport_write_message(NULL, fileno(f), topt, + 0, dsnnotifyhdr, NULL, NULL, NULL, NULL, 0); + fflush(f); + + /* we never add the final text. close the file */ + if (emf) + (void)fclose(emf); + + fprintf(f, "\n--%s--\n", boundaryStr); +#endif /*EXPERIMENTAL_DSN*/ /* Close the file, which should send an EOF to the child process that is receiving the message. Wait for it to finish. */ @@ -6873,7 +7182,7 @@ if (addr_defer == NULL) if ((log_extra_selector & LX_queue_time_overall) != 0) log_write(0, LOG_MAIN, "Completed QT=%s", - readconf_printtime(time(NULL) - received_time)); + readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) ); else log_write(0, LOG_MAIN, "Completed"); @@ -6996,6 +7305,10 @@ else if (addr_defer != (address_item *)(+1)) it also defers). */ if (!queue_2stage && delivery_attempted && +#ifdef EXPERIMENTAL_DSN + (((addr_defer->dsn_flags & rf_dsnflags) == 0) || + (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay) && +#endif delay_warning[1] > 0 && sender_address[0] != 0 && (delay_warning_condition == NULL || expand_check_condition(delay_warning_condition, @@ -7060,8 +7373,11 @@ else if (addr_defer != (address_item *)(+1)) uschar *wmf_text; FILE *wmf = NULL; FILE *f = fdopen(fd, "wb"); +#ifdef EXPERIMENTAL_DSN + uschar boundaryStr[64]; +#endif - if (warn_message_file != NULL) + if (warn_message_file) { wmf = Ufopen(warn_message_file, "rb"); if (wmf == NULL) @@ -7074,21 +7390,39 @@ else if (addr_defer != (address_item *)(+1)) string_sprintf("%d minutes", show_time/60): string_sprintf("%d hours", show_time/3600); - if (errors_reply_to != NULL) + if (errors_reply_to) fprintf(f, "Reply-To: %s\n", errors_reply_to); fprintf(f, "Auto-Submitted: auto-replied\n"); moan_write_from(f); fprintf(f, "To: %s\n", recipients); - wmf_text = next_emf(wmf, US"header"); - if (wmf_text != NULL) +#ifdef EXPERIMENTAL_DSN + /* generated boundary string and output MIME-Headers */ + snprintf(boundaryStr, sizeof(boundaryStr)-1, + TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); + + fprintf(f, "Content-Type: multipart/report;" + " report-type=delivery-status; boundary=%s\n" + "MIME-Version: 1.0\n", + boundaryStr); +#endif + + if ((wmf_text = next_emf(wmf, US"header"))) fprintf(f, "%s\n", wmf_text); else fprintf(f, "Subject: Warning: message %s delayed %s\n\n", message_id, warnmsg_delay); - wmf_text = next_emf(wmf, US"intro"); - if (wmf_text != NULL) fprintf(f, "%s", CS wmf_text); else +#ifdef EXPERIMENTAL_DSN + /* output human readable part as text/plain section */ + fprintf(f, "--%s\n" + "Content-type: text/plain; charset=us-ascii\n\n", + boundaryStr); +#endif + + if ((wmf_text = next_emf(wmf, US"intro"))) + fprintf(f, "%s", CS wmf_text); + else { fprintf(f, "This message was created automatically by mail delivery software.\n"); @@ -7098,49 +7432,52 @@ else if (addr_defer != (address_item *)(+1)) "A message that you sent has not yet been delivered to one or more of its\n" "recipients after more than "); - else fprintf(f, + else + fprintf(f, "A message sent by\n\n <%s>\n\n" "has not yet been delivered to one or more of its recipients after more than \n", - sender_address); + sender_address); - fprintf(f, "%s on the queue on %s.\n\n", warnmsg_delay, - primary_hostname); - fprintf(f, "The message identifier is: %s\n", message_id); + fprintf(f, "%s on the queue on %s.\n\n" + "The message identifier is: %s\n", + warnmsg_delay, primary_hostname, message_id); for (h = header_list; h != NULL; h = h->next) - { if (strncmpic(h->text, US"Subject:", 8) == 0) fprintf(f, "The subject of the message is: %s", h->text + 9); else if (strncmpic(h->text, US"Date:", 5) == 0) fprintf(f, "The date of the message is: %s", h->text + 6); - } - fprintf(f, "\n"); + fputc('\n', f); fprintf(f, "The address%s to which the message has not yet been " "delivered %s:\n", - (addr_defer->next == NULL)? "" : "es", - (addr_defer->next == NULL)? "is": "are"); + !addr_defer->next ? "" : "es", + !addr_defer->next ? "is": "are"); } /* List the addresses, with error information if allowed */ - fprintf(f, "\n"); - while (addr_defer != NULL) +#ifdef EXPERIMENTAL_DSN + /* store addr_defer for machine readable part */ + address_item *addr_dsndefer = addr_defer; +#endif + fputc('\n', f); + while (addr_defer) { address_item *addr = addr_defer; addr_defer = addr->next; if (print_address_information(addr, f, US" ", US"\n ", US"")) print_address_error(addr, f, US"Delay reason: "); - fprintf(f, "\n"); + fputc('\n', f); } - fprintf(f, "\n"); + fputc('\n', f); /* Final text */ - if (wmf != NULL) + if (wmf) { - wmf_text = next_emf(wmf, US"final"); - if (wmf_text != NULL) fprintf(f, "%s", CS wmf_text); + if ((wmf_text = next_emf(wmf, US"final"))) + fprintf(f, "%s", CS wmf_text); (void)fclose(wmf); } else @@ -7152,6 +7489,58 @@ else if (addr_defer != (address_item *)(+1)) "and when that happens, the message will be returned to you.\n"); } +#ifdef EXPERIMENTAL_DSN + /* output machine readable part */ + fprintf(f, "\n--%s\n" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + boundaryStr, + smtp_active_hostname); + + + if (dsn_envid) + { + /* must be decoded from xtext: see RFC 3461:6.3a */ + uschar *xdec_envid; + if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) + fprintf(f,"Original-Envelope-ID: %s\n", dsn_envid); + else + fprintf(f,"X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); + } + fputc('\n', f); + + while (addr_dsndefer) + { + if (addr_dsndefer->dsn_orcpt) + fprintf(f,"Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt); + + fprintf(f,"Action: delayed\n"); + fprintf(f,"Final-Recipient: rfc822;%s\n", addr_dsndefer->address); + fprintf(f,"Status: 4.0.0\n"); + if (addr_dsndefer->host_used && addr_dsndefer->host_used->name) + fprintf(f,"Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n", + addr_dsndefer->host_used->name, addr_dsndefer->basic_errno); + addr_dsndefer = addr_dsndefer->next; + } + + fprintf(f, "\n--%s\n" + "Content-type: text/rfc822-headers\n\n", + boundaryStr); + + fflush(f); + /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ + int topt = topt_add_return_path | topt_no_body; + transport_filter_argv = NULL; /* Just in case */ + return_path = sender_address; /* In case not previously set */ + /* Write the original email out */ + transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); + fflush(f); + + fprintf(f,"\n--%s--\n", boundaryStr); + + fflush(f); +#endif /*EXPERIMENTAL_DSN*/ + /* Close and wait for child process to complete, without a timeout. If there's an error, don't update the count. */