-#ifdef EXPERIMENTAL_TPDA
-int
-tpda_raise_event(uschar * action, uschar * event, uschar * ev_data)
+#ifdef EXPERIMENTAL_EVENT
+uschar *
+event_raise(uschar * action, uschar * event, uschar * ev_data)
{
uschar * s;
if (action)
{
DEBUG(D_deliver)
- debug_printf("TPDA(%s): tpda_event_action=|%s| tpda_delivery_IP=%s\n",
+ debug_printf("Event(%s): event_action=|%s| delivery_IP=%s\n",
event,
action, deliver_host_address);
- tpda_event = event;
- tpda_data = ev_data;
+ event_name = event;
+ event_data = ev_data;
if (!(s = expand_string(action)) && *expand_string_message)
log_write(0, LOG_MAIN|LOG_PANIC,
- "failed to expand tpda_event_action %s in %s: %s\n",
+ "failed to expand event_action %s in %s: %s\n",
event, transport_name, expand_string_message);
- tpda_event = tpda_data = NULL;
+ event_name = event_data = NULL;
/* If the expansion returns anything but an empty string, flag for
the caller to modify his normal processing
if (s && *s)
{
DEBUG(D_deliver)
- debug_printf("TPDA(%s): event_action returned \"%s\"\n", event, s);
- return DEFER;
+ debug_printf("Event(%s): event_action returned \"%s\"\n", event, s);
+ return s;
}
}
-return OK;
+return NULL;
}
static void
-tpda_msg_event(uschar * event, address_item * addr)
+msg_event_raise(uschar * event, address_item * addr)
{
uschar * save_domain = deliver_domain;
uschar * save_local = deliver_localpart;
+uschar * save_host = deliver_host;
if (!addr->transport)
return;
transport_name = addr->transport->name;
deliver_domain = addr->domain;
deliver_localpart = addr->local_part;
+deliver_host = addr->host_used ? addr->host_used->name : NULL;
-(void) tpda_raise_event(addr->transport->tpda_event_action, event,
+(void) event_raise(addr->transport->event_action, event,
addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
? addr->message : NULL);
+deliver_host = save_host;
deliver_localpart = save_local;
deliver_domain = save_domain;
router_name = transport_name = NULL;
}
-#endif /*EXPERIMENTAL_TPDA*/
+#endif /*EXPERIMENTAL_EVENT*/
uschar *s; /* building log lines; */
void *reset_point; /* released afterwards. */
-
/* Log the delivery on the main log. We use an extensible string to build up
the log line, and reset the store afterwards. Remote deliveries should always
have a pointer to the host item that succeeded; local deliveries can have a
pointer to a single host item in their host list, for use by the transport. */
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
/* presume no successful remote delivery */
lookup_dnssec_authenticated = NULL;
#endif
if (continue_sequence > 1)
s = string_cat(s, &size, &ptr, US"*", 1);
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
deliver_host_address = addr->host_used->address;
deliver_host_port = addr->host_used->port;
+ deliver_host = addr->host_used->name;
/* DNS lookup status */
lookup_dnssec_authenticated = addr->host_used->dnssec==DS_YES ? US"yes"
s[ptr] = 0;
log_write(0, flags, "%s", s);
-#ifdef EXPERIMENTAL_TPDA
-/*XXX cutthrough calls this also for non-delivery...*/
-tpda_msg_event(US"msg:delivery", addr);
+#ifdef EXPERIMENTAL_EVENT
+if (!msg) msg_event_raise(US"msg:delivery", addr);
#endif
store_reset(reset_point);
uschar *s; /* building log lines; */
void *reset_point; /* released afterwards. */
-
DEBUG(D_deliver) debug_printf("post-process %s (%d)\n", addr->address, result);
/* Set up driver kind and name for logging. Disable logging if the router or
child_done(addr, now);
}
- /* Certificates for logging (via TPDA) */
+ /* Certificates for logging (via events) */
#ifdef SUPPORT_TLS
tls_out.ourcert = addr->ourcert;
addr->ourcert = NULL;
s = string_append(s, &size, &ptr, 2, US": ",
US strerror(addr->basic_errno));
+ if (addr->host_used)
+ s = string_append(s, &size, &ptr, 5,
+ US" H=", addr->host_used->name,
+ US" [", addr->host_used->address, US"]");
+
if (addr->message != NULL)
s = string_append(s, &size, &ptr, 2, US": ", addr->message);
log_write(0, LOG_MAIN, "** %s", s);
-#ifdef EXPERIMENTAL_TPDA
- tpda_msg_event(US"msg:fail:delivery", addr);
+#ifdef EXPERIMENTAL_EVENT
+ msg_event_raise(US"msg:fail:delivery", addr);
#endif
store_reset(reset_point);
}
/* 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
string_printing(original));
}
+if (addr->host_used)
+ fprintf(f, "\n host %s [%s]",
+ addr->host_used->name, addr->host_used->address);
+
fprintf(f, "%s", CS se);
return yield;
}
}
+#ifdef EXPERIMENTAL_DSN
+/***********************************************************
+* 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);
+}
+#endif /* EXPERIMENTAL_DSN */
/*************************************************
break;
}
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
if (process_recipients != RECIP_ACCEPT)
{
uschar * save_local = deliver_localpart;
deliver_domain = expand_string(
string_sprintf("${domain:%s}", new->address));
- (void) tpda_raise_event(delivery_event_action,
+ (void) event_raise(event_action,
US"msg:fail:internal", new->message);
deliver_localpart = save_local;
{
uschar *s = (addr_failed->user_message != NULL)?
addr_failed->user_message : addr_failed->message;
+ host_item * host;
fprintf(stderr, "Delivery failed: ");
if (addr_failed->basic_errno > 0)
fprintf(stderr, "%s", strerror(addr_failed->basic_errno));
if (s != NULL) fprintf(stderr, ": ");
}
+ if ((host = addr_failed->host_used))
+ fprintf(stderr, "H=%s [%s]: ", host->name, host->address);
if (s == NULL)
{
if (addr_failed->basic_errno <= 0) fprintf(stderr, "unknown error");
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);
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;
#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);
+ bound);
#endif
/* Open a template file if one is provided. Log failure to open, but
/* output human readable part as text/plain section */
fprintf(f, "--%s\n"
"Content-type: text/plain; charset=us-ascii\n\n",
- boundaryStr);
+ bound);
#endif
if ((emf_text = next_emf(emf, US"intro")))
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
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);
+ fprintf(f, "\n--%s--\n", bound);
#endif /*EXPERIMENTAL_DSN*/
/* Close the file, which should send an EOF to the child process
/* Unset deliver_freeze so that we won't try to move the spool files further down */
deliver_freeze = FALSE;
-#ifdef EXPERIMENTAL_TPDA
- (void) tpda_raise_event(delivery_event_action, US"msg:complete", NULL);
+#ifdef EXPERIMENTAL_EVENT
+ (void) event_raise(event_action, US"msg:complete", NULL);
#endif
}
FILE *wmf = NULL;
FILE *f = fdopen(fd, "wb");
#ifdef EXPERIMENTAL_DSN
- uschar boundaryStr[64];
+ uschar * bound;
#endif
if (warn_message_file)
#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);
+ bound);
#endif
if ((wmf_text = next_emf(wmf, US"header")))
/* output human readable part as text/plain section */
fprintf(f, "--%s\n"
"Content-type: text/plain; charset=us-ascii\n\n",
- boundaryStr);
+ bound);
#endif
if ((wmf_text = next_emf(wmf, US"intro")))
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*/