* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* SPDX-License-Identifier: GPL-2.0-or-later */
{
while (addr->parent)
{
- address_item *aa;
+ address_item * aa;
addr = addr->parent;
if (--addr->child_count > 0) return; /* Incomplete parent */
/* string_cat() always leaves room for the terminator. Release the
store we used to build the line after writing it. */
-log_write(0, flags, "%s", string_from_gstring(g));
+log_write(0, flags, "%Y", g);
#ifndef DISABLE_EVENT
if (!msg) msg_event_raise(US"msg:delivery", addr);
if (addr->message)
g = string_append(g, 2, US": ", addr->message);
- {
- const uschar * s = string_from_gstring(g);
+/* Log the deferment in the message log, but don't clutter it
+up with retry-time defers after the first delivery attempt. */
- /* Log the deferment in the message log, but don't clutter it
- up with retry-time defers after the first delivery attempt. */
+if (f.deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE)
+ deliver_msglog("%s %.*s\n", now, g->ptr, g->s);
- if (f.deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE)
- deliver_msglog("%s %s\n", now, s);
+/* Write the main log and reset the store.
+For errors of the type "retry time not reached" (also remotes skipped
+on queue run), logging is controlled by L_retry_defer. Note that this kind
+of error number is negative, and all the retry ones are less than any
+others. */
- /* Write the main log and reset the store.
- For errors of the type "retry time not reached" (also remotes skipped
- on queue run), logging is controlled by L_retry_defer. Note that this kind
- of error number is negative, and all the retry ones are less than any
- others. */
-
- log_write(addr->basic_errno <= ERRNO_RETRY_BASE ? L_retry_defer : 0, logflags,
- "== %s", s);
- }
+log_write(addr->basic_errno <= ERRNO_RETRY_BASE ? L_retry_defer : 0, logflags,
+ "== %Y", g);
store_reset(reset_point);
return;
/* Do the logging. For the message log, "routing failed" for those cases,
just to make it clearer. */
- {
- const uschar * s = string_from_gstring(g);
-
- if (driver_kind)
- deliver_msglog("%s %s failed for %s\n", now, driver_kind, s);
- else
- deliver_msglog("%s %s\n", now, s);
+if (driver_kind)
+ deliver_msglog("%s %s failed for %.*s\n", now, driver_kind, g->ptr, g->s);
+else
+ deliver_msglog("%s %.*s\n", now, g->ptr, g->s);
- log_write(0, LOG_MAIN, "** %s", s);
- }
+log_write(0, LOG_MAIN, "** %Y", g);
store_reset(reset_point);
return;
addr->local_part, tp->name);
/* Setting these globals in the subprocess means we need never clear them */
- transport_name = addr->transport->name;
+
+ transport_name = tp->name;
+ if (addr->router) router_name = addr->router->name;
driver_srcfile = tp->srcfile;
driver_srcline = tp->srcline;
{
ok = transport_set_up_command(&transport_filter_argv,
tp->filter_command,
- TRUE, PANIC, addr, FALSE, US"transport filter", NULL);
+ TSUC_EXPAND_ARGS, PANIC, addr, US"transport filter", NULL);
transport_filter_timeout = tp->filter_timeout;
}
else transport_filter_argv = NULL;
pipeheader[PIPE_HEADER_SIZE] = '\0';
DEBUG(D_deliver)
- debug_printf("got %ld bytes (pipeheader) from transport process %d\n",
- (long) got, pid);
+ debug_printf("got %ld bytes (pipeheader) '%c' from transport process %d\n",
+ (long) got, *id, pid);
{
/* If we can't decode the pipeheader, the subprocess seems to have a
/* Put the amount of data written into the parlist block */
- case 'S':
+ case 'S': /* Size */
memcpy(&(p->transport_count), ptr, sizeof(transport_count));
ptr += sizeof(transport_count);
break;
if (*subid > '1') setflag(addr, af_tcp_fastopen_data);
break;
- case 'D':
+ case 'D': /* DSN */
if (!addr) goto ADDR_MISMATCH;
memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware));
ptr += sizeof(addr->dsn_aware);
host_item *h;
/* Setting these globals in the subprocess means we need never clear them */
- transport_name = addr->transport->name;
+
+ transport_name = tp->name;
+ if (addr->router) router_name = addr->router->name;
driver_srcfile = tp->srcfile;
driver_srcline = tp->srcline;
fprintf(f, "\n "); /* sic (because space follows) */
count = 0;
}
+ else if (count > 254) /* arbitrary limit */
+ {
+ fprintf(f, "[truncated]");
+ do s++; while (*s && !(*s == '\\' && s[1] == '\n'));
+ }
}
}
static void
dsn_put_wrapped(FILE * fp, const uschar * header, const uschar * s)
{
-const uschar * t;
-int llen = fprintf(fp, "%s", CS header), sleft = Ustrlen(s);
-int remain = 1022 - llen;
-
-if (*s && remain > 0)
- {
- for(;;)
- {
- unsigned ltail; /* source chars to skip */
-
- /* Chop at a newline, or end of string */
-
- if ((t = Ustrchr(s, '\\')) && t[1] == 'n')
- ltail = 2;
- else if ((t = Ustrchr(s, '\n')))
- ltail = 1;
- else
- {
- t = s + sleft;
- ltail = 0;
- }
-
- /* If that is too long, search backward for a space */
+gstring * g = string_cat(NULL, header);
- if ((llen + t - s) > 78)
- {
- const uschar * u;
- for (u = s + 78 - llen; u > s + 10; --u) if (*u == ' ') break;
- if (u > s + 10)
- { /* found a space to linebreak at */
- llen = u - s;
- remain -= fprintf(fp, "%.*s", (int)llen, s);
- s += ++llen; /* skip the space also */
- }
- else if (llen < 78)
- { /* just linebreak at 78 */
- llen = 78 - llen;
- remain -= fprintf(fp, "%.*s", llen, s);
- s += llen;
- }
- else /* header rather long */
- llen = 0;
- }
- else
- {
- llen = t - s;
- remain -= fprintf(fp, "%.*s", llen, s);
- s = t + ltail;
- }
+g = string_cat(g, s);
+gstring_release_unused(g);
+fprintf(fp, "%s\n", wrap_header(string_from_gstring(g), 79, 1023, US" ", 1));
+}
- sleft -= llen;
- remain -= 2;
- if (!*s || remain <= 0)
- break;
- fputs("\n ", fp);
- llen = 1; /* one for the leading space output above */
- }
- if (s[-1] != '\n') fputs("\n", fp);
- }
-else
- fputs("\n", fp);
-}
/*************************************************
if (addr->return_file >= 0)
{
- paddr = &(addr->next);
+ paddr = &addr->next;
filecount++;
}
for (address_item * addr = handled_addr; addr; addr = addr->next)
{
host_item * hu;
+#ifdef EXPERIMENTAL_DSN_INFO
+ const uschar * s;
+#endif
print_dsn_addr_action(fp, addr, US"failed", US"5.0.0");
{
fprintf(fp, "Remote-MTA: dns; %s\n", hu->name);
#ifdef EXPERIMENTAL_DSN_INFO
- {
- const uschar * s;
if (hu->address)
{
uschar * p = hu->port == 25
dsn_put_wrapped(fp, US"X-Remote-MTA-smtp-greeting: X-str; ", s);
if ((s = addr->helo_response) && *s)
dsn_put_wrapped(fp, US"X-Remote-MTA-helo-response: X-str; ", s);
- if ((s = addr->message) && *s)
+ if (testflag(addr, af_pass_message) && (s = addr->message) && *s)
dsn_put_wrapped(fp, US"X-Exim-Diagnostic: X-str; ", s);
- }
#endif
print_dsn_diagnostic_code(addr, fp);
}
+#ifdef EXPERIMENTAL_DSN_INFO
+ else if (testflag(addr, af_pass_message) && (s = addr->message) && *s)
+ dsn_put_wrapped(fp, US"X-Exim-Diagnostic: X-str; ", s);
+#endif
fputc('\n', fp);
}
tctx.u.fd = fileno(fp);
tctx.tblock = &tb;
- tctx.options = topt;
+ tctx.options = topt | topt_truncate_headers;
tb.add_headers = dsnnotifyhdr;
/*XXX no checking for failure! buggy! */
if (rc != 0)
{
- uschar *s = US"";
+ uschar * s = US"";
if (now - received_time.tv_sec < retry_maximum_timeout && !addr_defer)
{
addr_defer = (address_item *)(+1);
fflush(f);
/* header only as required by RFC. only failure DSN needs to honor RET=FULL */
tctx.u.fd = fileno(f);
-tctx.options = topt_add_return_path | topt_no_body;
+tctx.options = topt_add_return_path | topt_truncate_headers | topt_no_body;
transport_filter_argv = NULL; /* Just in case */
return_path = sender_address; /* In case not previously set */
*************************************************/
static void
-maybe_send_dsn(void)
+maybe_send_dsn(const address_item * const addr_succeed)
{
address_item * addr_senddsn = NULL;
-for (address_item * a = addr_succeed; a; a = a->next)
+for (const address_item * a = addr_succeed; a; a = a->next)
{
/* af_ignore_error not honored here. it's not an error */
DEBUG(D_deliver) debug_printf("DSN: processing router : %s\n"
/* Write the original email out */
tctx.u.fd = fd;
- tctx.options = topt_add_return_path | topt_no_body;
+ tctx.options = topt_add_return_path | topt_truncate_headers | topt_no_body;
/*XXX hmm, FALSE(fail) retval ignored.
Could error for any number of reasons, and they are not handled. */
transport_write_message(&tctx, 0);
*/
int
-deliver_message(uschar *id, BOOL forced, BOOL give_up)
+deliver_message(const uschar * id, BOOL forced, BOOL give_up)
{
int i, rc;
int final_yield = DELIVER_ATTEMPTED_NORMAL;
if ((deliver_datafile = spool_open_datafile(id)) < 0)
return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */
-/* The value of message_size at this point has been set to the data length,
+/* tHe value of message_size at this point has been set to the data length,
plus one for the blank line that notionally precedes the data. */
/* Now read the contents of the header file, which will set up the headers in
if (rc != spool_read_hdrerror)
{
received_time.tv_sec = received_time.tv_usec = 0;
- /*XXX subsec precision?*/
- for (i = 0; i < 6; i++)
+ /*III subsec precision?*/
+ for (i = 0; i < MESSAGE_ID_TIME_LEN; i++)
received_time.tv_sec = received_time.tv_sec * BASE_62 + tab62[id[i] - '0'];
}
for (i = 0; i < recipients_count; i++)
if (!tree_search(tree_nonrecipients, recipients_list[i].address))
{
- recipient_item *r = recipients_list + i;
- address_item *new = deliver_make_addr(r->address, FALSE);
+ recipient_item * r = recipients_list + i;
+ address_item * new = deliver_make_addr(r->address, FALSE);
+
new->prop.errors_address = r->errors_to;
#ifdef SUPPORT_I18N
if ((new->prop.utf8_msg = message_smtputf8))
case RECIP_FAIL:
new->message = US"delivery cancelled by administrator";
+ /* not setting af_pass_message here means that will not
+ appear in the bounce message */
/* Fall through */
/* Common code for the failure cases above. If this is not a bounce
The incident has already been logged. */
RECIP_QUEUE_FAILED:
- if (sender_address[0])
+ if (*sender_address)
{
new->next = addr_failed;
addr_failed = new;
/* Send DSN for successful messages if requested */
-maybe_send_dsn();
+maybe_send_dsn(addr_succeed);
/* 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