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;
{
if (addr == NULL)
{
- uschar ***p = address_expansions;
+ const uschar ***p = address_expansions;
while (*p != NULL) **p++ = NULL;
return;
}
#ifdef EXPERIMENTAL_EVENT
uschar *
-event_raise(uschar * action, uschar * event, uschar * ev_data)
+event_raise(uschar * action, const uschar * event, uschar * ev_data)
{
uschar * s;
if (action)
}
static void
-msg_event_raise(uschar * event, address_item * addr)
+msg_event_raise(const uschar * event, const address_item * addr)
{
-uschar * save_domain = deliver_domain;
+const uschar * save_domain = deliver_domain;
uschar * save_local = deliver_localpart;
-uschar * save_host = deliver_host;
+const uschar * save_host = deliver_host;
if (!addr->transport)
return;
s = string_append(s, &size, &ptr, 2, US"> ", log_address);
}
+if (log_extra_selector & LX_incoming_interface && sending_ip_address)
+ s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]");
+ /* for the port: string_sprintf("%d", sending_port) */
+
if ((log_extra_selector & LX_sender_on_delivery) != 0 || msg)
s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">");
addr->message &&
(addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0))
{
- int i;
+ unsigned i;
+ unsigned lim = big_buffer_size < 1024 ? big_buffer_size : 1024;
uschar *p = big_buffer;
uschar *ss = addr->message;
*p++ = '\"';
- for (i = 0; i < 256 && ss[i] != 0; i++) /* limit logged amount */
+ for (i = 0; i < lim && ss[i] != 0; i++) /* limit logged amount */
{
if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */
*p++ = ss[i];
if (addr->message != NULL)
{
- addr->message = string_printing(addr->message);
+ const uschar * s = string_printing(addr->message);
+ if (s != addr->message)
+ addr->message = US s;
+ /* deconst cast ok as string_printing known to have alloc'n'copied */
if (((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) &&
(Ustrstr(addr->message, "mysql") != NULL ||
Ustrstr(addr->message, "pgsql") != NULL ||
if (s != NULL)
{
uschar *p = big_buffer + Ustrlen(big_buffer);
+ const uschar * sp;
while (p > big_buffer && isspace(p[-1])) p--;
*p = 0;
- s = string_printing(big_buffer);
+ sp = string_printing(big_buffer);
log_write(0, LOG_MAIN, "<%s>: %s transport output: %s",
- addr->address, tb->name, s);
+ addr->address, tb->name, sp);
}
(void)fclose(f);
}
{
int sep = 0;
address_item **aptr = &addr_remote;
-uschar *listptr = remote_sort_domains;
+const uschar *listptr = remote_sort_domains;
uschar *pattern;
uschar patbuf[256];
{
address_item **next;
deliver_domain = (*aptr)->domain; /* set $domain */
- if (match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1,
+ if (match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1,
&domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)
{
aptr = &((*aptr)->next);
next = &((*aptr)->next);
while (*next != NULL &&
(deliver_domain = (*next)->domain, /* Set $domain */
- match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1,
+ match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1,
&domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) != OK)
next = &((*next)->next);
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)
addr = addr->next;
break;
+ /* Local interface address/port */
+ case 'I':
+ if (*ptr) sending_ip_address = string_copy(ptr);
+ while (*ptr++) ;
+ if (*ptr) sending_port = atoi(CS ptr);
+ while (*ptr++) ;
+ break;
+
/* Z marks the logical end of the data. It is followed by '0' if
continue_transport was NULL at the end of transporting, otherwise '1'.
We need to know when it becomes NULL during a delivery down a passed SMTP
}
/* 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. */
rmt_dlv_checked_write(fd, 'A', '0', big_buffer, ptr - big_buffer);
}
+ /* Local interface address/port */
+ if (log_extra_selector & LX_incoming_interface && sending_ip_address)
+ {
+ uschar * ptr = big_buffer;
+ sprintf(CS ptr, "%.128s", sending_ip_address);
+ while(*ptr++);
+ sprintf(CS ptr, "%d", sending_port);
+ while(*ptr++);
+
+ rmt_dlv_checked_write(fd, 'I', '0', big_buffer, ptr - big_buffer);
+ }
+
/* Add termination flag, close the pipe, and that's it. The character
after 'Z' indicates whether continue_transport is now NULL or not.
A change from non-NULL to NULL indicates a problem with a continuing
deliver_domain = addr->domain; /* set $domain */
- while ((rc = match_isinlist(deliver_domain, &percent_hack_domains, 0,
+ while ((rc = match_isinlist(deliver_domain, (const uschar **)&percent_hack_domains, 0,
&domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
== OK &&
(t = Ustrrchr(local_part, '%')) != 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);
+}
/*************************************************
recipient_item *r = recipients_list + i;
address_item *new = deliver_make_addr(r->address, FALSE);
new->p.errors_address = r->errors_to;
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ new->p.utf8 = message_smtputf8;
+ DEBUG(D_deliver) debug_printf("utf8: %c\n", message_smtputf8 ? 'T':'F');
+#endif
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)
{
if (process_recipients != RECIP_ACCEPT)
{
uschar * save_local = deliver_localpart;
- uschar * save_domain = deliver_domain;
+ const uschar * save_domain = deliver_domain;
deliver_localpart = expand_string(
string_sprintf("${local_part:%s}", new->address));
deliver_domain = addr->domain; /* set $domain */
if (!forced && hold_domains != NULL &&
- (rc = match_isinlist(addr->domain, &hold_domains, 0,
+ (rc = match_isinlist(addr->domain, (const uschar **)&hold_domains, 0,
&domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE,
NULL)) != FAIL)
{
addr_route = addr->next;
deliver_domain = addr->domain; /* set $domain */
- if ((rc = match_isinlist(addr->domain, &queue_domains, 0,
+ if ((rc = match_isinlist(addr->domain, (const uschar **)&queue_domains, 0,
&domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
!= OK)
{
{
int rc;
address_item *addr = addr_route;
- uschar *old_domain = addr->domain;
+ const uschar *old_domain = addr->domain;
uschar *old_unique = addr->unique;
addr_route = addr->next;
addr->next = NULL;
/* Precompile some regex that are used to recognize parameters in response
to an EHLO command, if they aren't already compiled. */
- if (regex_PIPELINING == NULL) regex_PIPELINING =
- regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
-
- if (regex_SIZE == NULL) regex_SIZE =
- regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
-
- if (regex_AUTH == NULL) regex_AUTH =
- regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
- FALSE, TRUE);
-
-#ifdef SUPPORT_TLS
- if (regex_STARTTLS == NULL) regex_STARTTLS =
- regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
-#endif
-
-#ifndef DISABLE_PRDR
- if (regex_PRDR == NULL) regex_PRDR =
- 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
+ deliver_init();
/* 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;
-while(addr_dsntmp != NULL)
+while(addr_dsntmp)
{
- 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);
+ DEBUG(D_deliver)
+ {
+ debug_printf("DSN: processing router : %s\n"
+ "DSN: processing successful delivery address: %s\n"
+ "DSN: Sender_address: %s\n"
+ "DSN: orcpt: %s flags: %d\n"
+ "DSN: envid: %s ret: %d\n"
+ "DSN: Final recipient: %s\n"
+ "DSN: Remote SMTP server supports DSN: %d\n",
+ addr_dsntmp->router->name,
+ addr_dsntmp->address,
+ sender_address,
+ addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags,
+ dsn_envid, dsn_ret,
+ addr_dsntmp->address,
+ 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)))
+ if ( ( addr_dsntmp->dsn_aware != dsn_support_yes
+ || addr_dsntmp->dsn_flags & rf_dsnlasthop
+ )
+ && addr_dsntmp->dsn_flags & rf_dsnflags
+ && addr_dsntmp->dsn_flags & rf_notify_success
+ )
{
/* copy and relink address_item and send report with all of them at once later */
address_item *addr_next;
addr_senddsn->next = addr_next;
}
else
- {
- DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message ***\n");
- }
+ DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n");
addr_dsntmp = addr_dsntmp->next;
}
-if (addr_senddsn != NULL)
+if (addr_senddsn)
{
pid_t pid;
int fd;
"create child process to send failure message: %s", getpid(),
getppid(), strerror(errno));
- DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n");
-
+ 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];
+ uschar * bound;
- DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address);
+ 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)
- {
+ for (addr_dsntmp = addr_senddsn; addr_dsntmp;
+ addr_dsntmp = addr_dsntmp->next)
fprintf(f, "<%s> (relayed %s)\n\n",
addr_dsntmp->address,
(addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1
? "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);
+ bound, smtp_active_hostname);
- if (dsn_envid != NULL) {
- /* must be decoded from xtext: see RFC 3461:6.3a */
+ 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);
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",
+ fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n\n",
addr_dsntmp->host_used->name);
else
- fprintf(f,"Diagnostic-Code: X-Exim; relayed via non %s router\n",
+ fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n",
(addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP");
- 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
if (sender_address[0] == 0 && addr_failed->p.errors_address == NULL)
{
- if (!testflag(addr_failed, af_retry_timedout) &&
- !testflag(addr_failed, af_ignore_error))
+ if ( !testflag(addr_failed, af_retry_timedout)
+ && !testflag(addr_failed, af_ignore_error))
{
log_write(0, LOG_MAIN|LOG_PANIC, "internal error: bounce message "
"failure is neither frozen nor ignored (it's been ignored)");
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
+ && (addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure)
)
- {
+ {
addr = addr_failed;
addr_failed = addr->next;
if (addr->return_filename != NULL) Uunlink(addr->return_filename);
log_write(0, LOG_MAIN, "%s%s%s%s: error ignored",
addr->address,
- (addr->parent == NULL)? US"" : US" <",
- (addr->parent == NULL)? US"" : addr->parent->address,
- (addr->parent == NULL)? US"" : US">");
+ !addr->parent ? US"" : US" <",
+ !addr->parent ? US"" : addr->parent->address,
+ !addr->parent ? US"" : US">");
address_done(addr, logtod);
child_done(addr, logtod);
else
{
- bounce_recipient = (addr_failed->p.errors_address == NULL)?
- sender_address : addr_failed->p.errors_address;
+ bounce_recipient = addr_failed->p.errors_address
+ ? addr_failed->p.errors_address : sender_address;
/* Make a subprocess to send a message */
- pid = child_open_exim(&fd);
-
- /* Creation of child failed */
-
- if (pid < 0)
+ if ((pid = child_open_exim(&fd)) < 0)
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));
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);
paddr = &addr_failed;
for (addr = addr_failed; addr != NULL; addr = *paddr)
- {
- if (Ustrcmp(bounce_recipient, (addr->p.errors_address == NULL)?
- sender_address : addr->p.errors_address) != 0)
- {
- paddr = &(addr->next); /* Not the same; skip */
- }
- else /* The same - dechain */
- {
+ if (Ustrcmp(bounce_recipient, addr->p.errors_address
+ ? addr->p.errors_address : sender_address) == 0)
+ { /* The same - dechain */
*paddr = addr->next;
*pmsgchain = addr;
addr->next = NULL;
pmsgchain = &(addr->next);
}
- }
+ else
+ paddr = &addr->next; /* Not the same; skip */
/* Include X-Failed-Recipients: for automatic interpretation, but do
not let any one header line get too long. We do this by starting a
/* Output the standard headers */
- 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", 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);
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ if (message_smtputf8)
+ fprintf(f, "--%s\n"
+ "Content-type: message/global-delivery-status\n\n"
+ "Reporting-MTA: dns; %s\n",
+ bound, smtp_active_hostname);
+ else
+#endif
+ fprintf(f, "--%s\n"
+ "Content-type: message/delivery-status\n\n"
+ "Reporting-MTA: dns; %s\n",
+ 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);
+ }
+ fputc('\n', 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, "--%s\n", bound);
dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned";
dsnnotifyhdr = NULL;
}
}
- if (topt & topt_no_body)
- fprintf(f,"Content-type: text/rfc822-headers\n\n");
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ if (message_smtputf8)
+ fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n"
+ : "Content-type: message/global\n\n",
+ f);
else
- fprintf(f,"Content-type: message/rfc822\n\n");
+#endif
+ fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n"
+ : "Content-type: message/rfc822\n\n",
+ f);
fflush(f);
transport_filter_argv = NULL; /* Just in case */
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. */
"msglog.OLD directory", spoolname);
}
else
- {
if (Uunlink(spoolname) < 0)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
spoolname, strerror(errno));
- }
}
/* Remove the two message files. */
if (deliver_domain != NULL)
{
- uschar *d = (testflag(addr, af_pfr))? addr->parent->domain : addr->domain;
+ const uschar *d = testflag(addr, af_pfr)
+ ? addr->parent->domain : addr->domain;
/* The domain may be unset for an address that has never been routed
because the system filter froze the message. */
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);
}
fputc('\n', f);
- while (addr_dsndefer)
+ for ( ; addr_dsndefer; addr_dsndefer = addr_dsndefer->next)
{
if (addr_dsndefer->dsn_orcpt)
- fprintf(f,"Original-Recipient: %s\n", 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");
+ fprintf(f, "Action: delayed\n"
+ "Final-Recipient: rfc822;%s\n"
+ "Status: 4.0.0\n",
+ addr_dsndefer->address);
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, "Remote-MTA: dns; %s\n",
+ addr_dsndefer->host_used->name);
+ print_dsn_diagnostic_code(addr_dsndefer, f);
+ }
+ fputc('\n', f);
}
- fprintf(f, "\n--%s\n"
+ fprintf(f, "--%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. */
return final_yield;
}
+
+
+void
+deliver_init(void)
+{
+if (!regex_PIPELINING) regex_PIPELINING =
+ regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_SIZE) regex_SIZE =
+ regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_AUTH) regex_AUTH =
+ regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
+ FALSE, TRUE);
+
+#ifdef SUPPORT_TLS
+if (!regex_STARTTLS) regex_STARTTLS =
+ regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+#ifndef DISABLE_PRDR
+if (!regex_PRDR) regex_PRDR =
+ regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+#ifdef SUPPORT_TLS
+if (!regex_UTF8) regex_UTF8 =
+ regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+if (!regex_DSN) regex_DSN =
+ regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
+ regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+}
+
+
+uschar *
+deliver_get_sender_address (uschar * id)
+{
+if (!spool_open_datafile(id))
+ return NULL;
+
+sprintf(CS spoolname, "%s-H", id);
+if (spool_read_header(spoolname, TRUE, TRUE) != spool_read_OK)
+ return NULL;
+
+(void)close(deliver_datafile);
+deliver_datafile = -1;
+
+return sender_address;
+}
+
/* vi: aw ai sw=2
*/
/* End of deliver.c */