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;
break;
#endif
-#ifdef EXPERIMENTAL_DSN
case 'D':
if (addr == NULL) goto ADDR_MISMATCH;
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)
}
/* Get the flag which specifies whether the transport can handle different
- domains that nevertheless resolve to the same set of hosts. */
-
- multi_domain = tp->multi_domain;
+ domains that nevertheless resolve to the same set of hosts. If it needs
+ expanding, get variables set: $address_data, $domain_data, $localpart_data,
+ $host, $host_address, $host_port. */
+ if (tp->expand_multi_domain)
+ deliver_set_expansions(addr);
+
+ if (exp_bool(addr, US"transport", tp->name, D_transport,
+ US"multi_domain", tp->multi_domain, tp->expand_multi_domain,
+ &multi_domain) != OK)
+ {
+ deliver_set_expansions(NULL);
+ remote_post_process(addr, LOG_MAIN|LOG_PANIC, addr->message, fallback);
+ continue;
+ }
/* Get the maximum it can handle in one envelope, with zero meaning
unlimited, which is forced for the MUA wrapper case. */
entirely different domains. The host list pointers can be NULL in the case
where the hosts are defined in the transport. There is also a configured
maximum limit of addresses that can be handled at once (see comments above
- for how it is computed). */
+ for how it is computed).
+ If the transport does not handle multiple domains, enforce that also,
+ and if it might need a per-address check for this, re-evaluate it.
+ */
while ((next = *anchor) != NULL && address_count < address_count_max)
{
- if ((multi_domain || Ustrcmp(next->domain, addr->domain) == 0)
- &&
- tp == next->transport
- &&
- same_hosts(next->host_list, addr->host_list)
- &&
- same_strings(next->p.errors_address, addr->p.errors_address)
- &&
- same_headers(next->p.extra_headers, addr->p.extra_headers)
- &&
- same_ugid(tp, next, addr)
- &&
- (next->p.remove_headers == addr->p.remove_headers ||
- (next->p.remove_headers != NULL &&
- addr->p.remove_headers != NULL &&
- Ustrcmp(next->p.remove_headers, addr->p.remove_headers) == 0)))
+ BOOL md;
+ if ( (multi_domain || Ustrcmp(next->domain, addr->domain) == 0)
+ && tp == next->transport
+ && same_hosts(next->host_list, addr->host_list)
+ && same_strings(next->p.errors_address, addr->p.errors_address)
+ && same_headers(next->p.extra_headers, addr->p.extra_headers)
+ && same_ugid(tp, next, addr)
+ && ( next->p.remove_headers == addr->p.remove_headers
+ || ( next->p.remove_headers != NULL
+ && addr->p.remove_headers != NULL
+ && Ustrcmp(next->p.remove_headers, addr->p.remove_headers) == 0
+ ) )
+ && ( !multi_domain
+ || ( (
+ !tp->expand_multi_domain || (deliver_set_expansions(next), 1),
+ exp_bool(addr,
+ US"transport", next->transport->name, D_transport,
+ US"multi_domain", next->transport->multi_domain,
+ next->transport->expand_multi_domain, &md) == OK
+ )
+ && md
+ ) ) )
{
*anchor = next->next;
next->next = NULL;
address_count++;
}
else anchor = &(next->next);
+ deliver_set_expansions(NULL);
}
/* If we are acting as an MUA wrapper, all addresses must go in a single
rmt_dlv_checked_write(fd, 'P', '0', NULL, 0);
#endif
-#ifdef EXPERIMENTAL_DSN
memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware));
rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware));
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. */
}
+/***********************************************************
+* Print Diagnostic-Code for an address *
+************************************************************/
+
+/* This function is called to print the error information out of an address for
+a bounce or a warning message. It tries to format the message reasonably as
+required by RFC 3461 by adding a space after each newline
+
+we assume that this function is only called if addr->host_used is set and if so
+a useable addr->message is available containing some Exim description with ": \n"
+ending, followed by the L/SMTP error message.
+
+Arguments:
+ addr the address
+ f the FILE to print on
+
+Returns: nothing
+*/
+
+static void
+print_dsn_diagnostic_code(const address_item *addr, FILE *f)
+{
+uschar * s;
+/* check host_used, af_pass_message flag and addr->message for safety reasons */
+if (!addr->host_used && testflag(addr, af_pass_message) && addr->message)
+ return;
+
+/* search first ": ". we assume to find the remote-MTA answer there */
+DEBUG(D_deliver)
+ debug_printf("DSN Diagnostic-Code: addr->dsn_message = %s\n", addr->message);
+if (!(s = Ustrstr(addr->message, ": ")))
+ return; /* not found, bail out */
+
+fprintf(f, "Diagnostic-Code: smtp; ");
+
+s += 2; /* skip ": " */
+while (*s)
+ if (*s == '\\' && s[1] == 'n')
+ {
+ fputs("\n ", f); /* as defined in RFC 3461 */
+ s += 2;
+ }
+ else
+ fputc(*s++, f);
+fputc('\n', f);
+}
/*************************************************
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)
{
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
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;
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];
+ uschar * bound;
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);
+ bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
+ DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", bound);
if (errors_reply_to)
fprintf(f, "Reply-To: %s\n", errors_reply_to);
"This message was created automatically by mail delivery software.\n"
" ----- The following addresses had successful delivery notifications -----\n",
- qualify_domain_sender, sender_address, boundaryStr, boundaryStr);
+ qualify_domain_sender, sender_address, bound, bound);
addr_dsntmp = addr_senddsn;
while(addr_dsntmp)
fprintf(f, "--%s\n"
"Content-type: message/delivery-status\n\n"
"Reporting-MTA: dns; %s\n",
- boundaryStr, smtp_active_hostname);
+ bound, smtp_active_hostname);
if (dsn_envid != NULL) {
/* must be decoded from xtext: see RFC 3461:6.3a */
fputc('\n', f);
}
- fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", boundaryStr);
+ fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound);
fflush(f);
transport_filter_argv = NULL; /* Just in case */
transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0);
fflush(f);
- fprintf(f,"\n");
- fprintf(f,"--%s--\n", boundaryStr);
+ fprintf(f,"\n--%s--\n", bound);
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
it from the list, throw away any saved message file, log it, and
mark the recipient done. */
- 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
+ if ( testflag(addr_failed, af_ignore_error)
+ || ( ((addr_failed->dsn_flags & rf_dsnflags) != 0)
+ && ((addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure))
)
{
addr = addr_failed;
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 * bound;
uschar *dsnlimitmsg;
uschar *dsnnotifyhdr;
int topt;
-#endif
DEBUG(D_deliver)
debug_printf("sending error message to: %s\n", bounce_recipient);
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());
+ bound = string_sprintf(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
+ bound);
/* Open a template file if one is provided. Log failure to open, but
carry on - default texts will be used. */
fprintf(f, "Subject: Mail delivery failed%s\n\n",
to_sender? ": returning message to sender" : "");
-#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
+ bound);
if ((emf_text = next_emf(emf, US"intro")))
fprintf(f, "%s", CS emf_text);
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);
+ bound, smtp_active_hostname);
if (dsn_envid)
{
"Status: 5.0.0\n",
addr->address);
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);
+ {
+ fprintf(f, "Remote-MTA: dns; %s\n",
+ addr->host_used->name);
+ print_dsn_diagnostic_code(addr, f);
+ }
}
-#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
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)
- 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");
- else fprintf(f,
-"------ This is a copy of the message's headers. ------\n");
- }
-
- /* While reading the "truncated" message, set return_size_limit to
- the actual max testing value, rounded. We need to read the message
- whether we are going to use it or not. */
-
- {
- int temp = bounce_return_size_limit;
- bounce_return_size_limit = (max/1000)*1000;
- emf_text = next_emf(emf, US"truncated");
- bounce_return_size_limit = temp;
- }
-
- if (bounce_return_body && bounce_return_size_limit > 0)
- {
- struct stat statbuf;
- if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
- {
- 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);
- }
- }
-
- 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,
- bounce_return_size_limit, NULL, NULL, NULL, NULL, NULL, 0);
- }
-
- /* Write final text and close the template file if one is open */
-
- if (emf)
- {
- 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_size_limit is always honored.
*/
- fprintf(f, "\n--%s\n", boundaryStr);
+ fprintf(f, "\n--%s\n", bound);
dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned";
dsnnotifyhdr = NULL;
if (emf)
(void)fclose(emf);
- fprintf(f, "\n--%s--\n", boundaryStr);
-#endif /*EXPERIMENTAL_DSN*/
+ fprintf(f, "\n--%s--\n", bound);
/* Close the file, which should send an EOF to the child process
that is receiving the message. Wait for it to finish. */
is not sent. Another attempt will be made at the next delivery attempt (if
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,
- US"delay_warning", US"option")))
+ if ( !queue_2stage
+ && delivery_attempted
+ && ( ((addr_defer->dsn_flags & rf_dsnflags) == 0)
+ || (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay
+ )
+ && delay_warning[1] > 0
+ && sender_address[0] != 0
+ && ( delay_warning_condition == NULL
+ || expand_check_condition(delay_warning_condition,
+ US"delay_warning", US"option")
+ )
+ )
{
int count;
int show_time;
uschar *wmf_text;
FILE *wmf = NULL;
FILE *f = fdopen(fd, "wb");
-#ifdef EXPERIMENTAL_DSN
- uschar boundaryStr[64];
-#endif
+ uschar * bound;
if (warn_message_file)
{
moan_write_from(f);
fprintf(f, "To: %s\n", recipients);
-#ifdef EXPERIMENTAL_DSN
/* generated boundary string and output MIME-Headers */
- snprintf(boundaryStr, sizeof(boundaryStr)-1,
- TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
+ bound = string_sprintf(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
+ bound);
if ((wmf_text = next_emf(wmf, US"header")))
fprintf(f, "%s\n", wmf_text);
fprintf(f, "Subject: Warning: message %s delayed %s\n\n",
message_id, warnmsg_delay);
-#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
+ bound);
if ((wmf_text = next_emf(wmf, US"intro")))
fprintf(f, "%s", CS wmf_text);
/* List the addresses, with error information if allowed */
-#ifdef EXPERIMENTAL_DSN
/* store addr_defer for machine readable part */
address_item *addr_dsndefer = addr_defer;
-#endif
fputc('\n', f);
while (addr_defer)
{
"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,
+ bound,
smtp_active_hostname);
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);
+ {
+ fprintf(f,"Remote-MTA: dns; %s\n",
+ addr_dsndefer->host_used->name);
+ print_dsn_diagnostic_code(addr_dsndefer, f);
+ }
addr_dsndefer = addr_dsndefer->next;
}
fprintf(f, "\n--%s\n"
"Content-type: text/rfc822-headers\n\n",
- boundaryStr);
+ bound);
fflush(f);
/* header only as required by RFC. only failure DSN needs to honor RET=FULL */
transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0);
fflush(f);
- fprintf(f,"\n--%s--\n", boundaryStr);
+ fprintf(f,"\n--%s--\n", bound);
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. */