static address_item *addr_remote = NULL;
static address_item *addr_route = NULL;
static address_item *addr_succeed = NULL;
-static address_item *addr_dsntmp = NULL;
static address_item *addr_senddsn = NULL;
static FILE *message_log = NULL;
else
{
- address_item *addr2;
if (testflag(addr, af_pfr))
{
if (testflag(addr, af_file)) address_file = addr->local_part;
else if (addr->local_part[0] == '|') address_pipe = addr->local_part;
}
- for (addr2 = addr->next; addr2; addr2 = addr2->next)
+ for (address_item * addr2 = addr->next; addr2; addr2 = addr2->next)
{
if (deliver_domain && Ustrcmp(deliver_domain, addr2->domain) != 0)
deliver_domain = NULL;
static int
open_msglog_file(uschar *filename, int mode, uschar **error)
{
-int fd, i;
-
-for (i = 2; i > 0; i--)
+for (int i = 2; i > 0; i--)
{
- fd = Uopen(filename,
+ int fd = Uopen(filename,
#ifdef O_CLOEXEC
O_CLOEXEC |
#endif
#ifndef O_CLOEXEC
(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
#endif
- if (fchown(fd, exim_uid, exim_gid) < 0)
+ if (exim_fchown(fd, exim_uid, exim_gid, filename) < 0)
{
*error = US"chown";
return -1;
MSGLOG_DIRECTORY_MODE, TRUE);
}
-*error = US"create";
+*error = US"create or open";
return -1;
}
static void
replicate_status(address_item *addr)
{
-address_item *addr2;
-for (addr2 = addr->next; addr2; addr2 = addr2->next)
+for (address_item * addr2 = addr->next; addr2; addr2 = addr2->next)
{
addr2->transport = addr->transport;
addr2->transport_return = addr->transport_return;
static void
address_done(address_item *addr, uschar *now)
{
-address_item *dup;
-
update_spool = TRUE; /* Ensure spool gets updated */
/* Top-level address */
/* Check the list of duplicate addresses and ensure they are now marked
done as well. */
-for (dup = addr_duplicate; dup; dup = dup->next)
+for (address_item * dup = addr_duplicate; dup; dup = dup->next)
if (Ustrcmp(addr->unique, dup->unique) == 0)
{
tree_add_nonrecipient(dup->unique);
static void
child_done(address_item *addr, uschar *now)
{
-address_item *aa;
while (addr->parent)
{
+ address_item *aa;
+
addr = addr->parent;
if (--addr->child_count > 0) return; /* Incomplete parent */
address_done(addr, now);
if (LOGGING(incoming_interface) && LOGGING(outgoing_interface)
&& sending_ip_address)
{
- g = string_append(g, 2, US" I=[", sending_ip_address);
- g = LOGGING(outgoing_port)
- ? string_append(g, 2, US"]:", string_sprintf("%d", sending_port))
- : string_catn(g, US"]", 1);
+ g = string_fmt_append(g, " I=[%s]", sending_ip_address);
+ if (LOGGING(outgoing_port))
+ g = string_fmt_append(g, ":%d", sending_port);
}
return g;
}
g = string_append(g, 3, US" [", h->address, US"]");
if (LOGGING(outgoing_port))
- g = string_append(g, 2, US":", string_sprintf("%d", h->port));
+ g = string_fmt_append(g, ":%d", h->port);
#ifdef SUPPORT_SOCKS
if (LOGGING(proxy) && proxy_local_address)
{
g = string_append(g, 3, US" PRX=[", proxy_local_address, US"]");
if (LOGGING(outgoing_port))
- g = string_append(g, 2, US":", string_sprintf("%d", proxy_local_port));
+ g = string_fmt_append(g, ":%d", proxy_local_port);
}
#endif
g = d_log_interface(g);
if (testflag(addr, af_tcp_fastopen))
- g = string_catn(g, US" TFO", 4);
+ g = string_catn(g, US" TFO*", testflag(addr, af_tcp_fastopen_data) ? 5 : 4);
return g;
}
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
static gstring *
-d_tlslog(gstring * s, address_item * addr)
+d_tlslog(gstring * g, address_item * addr)
{
if (LOGGING(tls_cipher) && addr->cipher)
- s = string_append(s, 2, US" X=", addr->cipher);
+ {
+ g = string_append(g, 2, US" X=", addr->cipher);
+#ifdef EXPERIMENTAL_TLS_RESUME
+ if (LOGGING(tls_resumption) && testflag(addr, af_tls_resume))
+ g = string_catn(g, US"*", 1);
+#endif
+ }
if (LOGGING(tls_certificate_verified) && addr->cipher)
- s = string_append(s, 2, US" CV=",
+ g = string_append(g, 2, US" CV=",
testflag(addr, af_cert_verified)
?
#ifdef SUPPORT_DANE
"yes"
: "no");
if (LOGGING(tls_peerdn) && addr->peerdn)
- s = string_append(s, 3, US" DN=\"", string_printing(addr->peerdn), US"\"");
-return s;
+ g = string_append(g, 3, US" DN=\"", string_printing(addr->peerdn), US"\"");
+return g;
}
#endif
if (!(s = expand_string(action)) && *expand_string_message)
log_write(0, LOG_MAIN|LOG_PANIC,
"failed to expand event_action %s in %s: %s\n",
- event, transport_name, expand_string_message);
+ event, transport_name ? transport_name : US"main", expand_string_message);
event_name = event_data = NULL;
{
DEBUG(D_deliver)
debug_printf("Event(%s): event_action returned \"%s\"\n", event, s);
+ errno = ERRNO_EVENT;
return s;
}
}
const uschar * save_address = deliver_host_address;
const int save_port = deliver_host_port;
-if (!addr->transport)
- return;
-
router_name = addr->router ? addr->router->name : NULL;
-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) event_raise(addr->transport->event_action, event,
- addr->host_used
- || Ustrcmp(addr->transport->driver_name, "smtp") == 0
- || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
- ? addr->message : NULL);
+if (!addr->transport)
+ {
+ if (Ustrcmp(event, "msg:fail:delivery") == 0)
+ {
+ /* An address failed with no transport involved. This happens when
+ a filter was used which triggered a fail command (in such a case
+ a transport isn't needed). Convert it to an internal fail event. */
+
+ (void) event_raise(event_action, US"msg:fail:internal", addr->message);
+ }
+ }
+else
+ {
+ transport_name = addr->transport->name;
+
+ (void) event_raise(addr->transport->event_action, event,
+ addr->host_used
+ || Ustrcmp(addr->transport->driver_name, "smtp") == 0
+ || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
+ || Ustrcmp(addr->transport->driver_name, "autoreply") == 0
+ ? addr->message : NULL);
+ }
deliver_host_port = save_port;
deliver_host_address = save_address;
&& addr->parent != topaddr)
{
uschar *s = US" (";
- address_item *addr2;
- for (addr2 = addr->parent; addr2 != topaddr; addr2 = addr2->parent)
+ for (address_item * addr2 = addr->parent; addr2 != topaddr; addr2 = addr2->parent)
{
g = string_catn(g, s, 2);
g = string_cat (g, addr2->address);
if (diff->tv_sec >= 5 || !LOGGING(millisec))
return readconf_printtime((int)diff->tv_sec);
-sprintf(CS buf, "%d.%03ds", (int)diff->tv_sec, (int)diff->tv_usec/1000);
+sprintf(CS buf, "%u.%03us", (uint)diff->tv_sec, (uint)diff->tv_usec/1000);
return buf;
}
g = string_append(g, 2, US" T=", addr->transport->name);
if (LOGGING(delivery_size))
- g = string_append(g, 2, US" S=",
- string_sprintf("%d", transport_count));
+ g = string_fmt_append(g, " S=%d", transport_count);
/* Local delivery */
#endif
}
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
g = d_tlslog(g, addr);
#endif
}
}
+ if (LOGGING(pipelining))
+ {
+ if (testflag(addr, af_pipelining))
+ g = string_catn(g, US" L", 2);
+#ifdef SUPPORT_PIPE_CONNECT
+ if (testflag(addr, af_early_pipe))
+ g = string_catn(g, US"*", 1);
+#endif
+ }
+
#ifndef DISABLE_PRDR
if (testflag(addr, af_prdr_used))
g = string_catn(g, US" PRDR", 5);
&& (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0)
)
{
- 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 < lim && ss[i] != 0; i++) /* limit logged amount */
+ for (int i = 0; i < lim && ss[i] != 0; i++) /* limit logged amount */
{
if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */
*p++ = ss[i];
{
if (driver_kind[1] == 't' && addr->router)
g = string_append(g, 2, US" R=", addr->router->name);
- g = string_cat(g, string_sprintf(" %c=%s", toupper(driver_kind[1]), driver_name));
+ g = string_fmt_append(g, " %c=%s", toupper(driver_kind[1]), driver_name);
}
else if (driver_kind)
g = string_append(g, 2, US" ", driver_kind);
-/*XXX need an s+s+p sprintf */
-g = string_cat(g, string_sprintf(" defer (%d)", addr->basic_errno));
+g = string_fmt_append(g, " defer (%d)", addr->basic_errno);
if (addr->basic_errno > 0)
g = string_append(g, 2, US": ",
if (LOGGING(outgoing_port))
{
int port = addr->host_used->port;
- g = string_append(g, 2,
- US":", port == PORT_NONE ? US"25" : string_sprintf("%d", port));
+ g = string_fmt_append(g, ":%d", port == PORT_NONE ? 25 : port);
}
}
/* Log the deferment in the message log, but don't clutter it
up with retry-time defers after the first delivery attempt. */
-if (deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE)
+if (f.deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE)
deliver_msglog("%s %s\n", now, g->s);
/* Write the main log and reset the store.
void * reset_point;
gstring * g = reset_point = string_get(256);
+#ifndef DISABLE_EVENT
+/* Message failures for which we will send a DSN get their event raised
+later so avoid doing it here. */
+
+if ( !addr->prop.ignore_error
+ && !(addr->dsn_flags & (rf_dsnflags & ~rf_notify_failure))
+ )
+ msg_event_raise(US"msg:fail:delivery", addr);
+#endif
+
/* Build up the log line for the message and main logs */
/* Create the address string for logging. Must not do this earlier, because
if (addr->host_used)
g = d_hostlog(g, addr);
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
g = d_tlslog(g, addr);
#endif
log_write(0, LOG_MAIN, "** %s", g->s);
-#ifndef DISABLE_EVENT
-msg_event_raise(US"msg:fail:delivery", addr);
-#endif
-
store_reset(reset_point);
return;
}
{
driver_name = addr->transport->name;
driver_kind = US" transport";
- disable_logging = addr->transport->disable_logging;
+ f.disable_logging = addr->transport->disable_logging;
}
else driver_kind = US"transporting";
}
{
driver_name = addr->router->name;
driver_kind = US" router";
- disable_logging = addr->router->disable_logging;
+ f.disable_logging = addr->router->disable_logging;
}
else driver_kind = US"routing";
}
}
/* Certificates for logging (via events) */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
tls_out.ourcert = addr->ourcert;
addr->ourcert = NULL;
tls_out.peercert = addr->peercert;
delivery_log(LOG_MAIN, addr, logchar, NULL);
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
tls_free_cert(&tls_out.ourcert);
tls_free_cert(&tls_out.peercert);
tls_out.cipher = NULL;
if (addr->special_action == SPECIAL_FREEZE)
{
- deliver_freeze = TRUE;
+ f.deliver_freeze = TRUE;
deliver_frozen_at = time(NULL);
update_spool = TRUE;
}
/* If doing a 2-stage queue run, we skip writing to either the message
log or the main log for SMTP defers. */
- if (!queue_2stage || addr->basic_errno != 0)
+ if (!f.queue_2stage || addr->basic_errno != 0)
deferral_log(addr, now, logflags, driver_name, driver_kind);
}
{
frozen_info = addr->special_action == SPECIAL_FREEZE
? US""
- : sender_local && !local_error_message
+ : f.sender_local && !f.local_error_message
? US" (message created with -f <>)"
: US" (delivery error message)";
- deliver_freeze = TRUE;
+ f.deliver_freeze = TRUE;
deliver_frozen_at = time(NULL);
update_spool = TRUE;
/* Ensure logging is turned on again in all cases */
-disable_logging = FALSE;
+f.disable_logging = FALSE;
}
static void
common_error(BOOL logit, address_item *addr, int code, uschar *format, ...)
{
-address_item *addr2;
addr->basic_errno = code;
if (format)
{
va_list ap;
- uschar buffer[512];
+ gstring * g;
+
va_start(ap, format);
- if (!string_vformat(buffer, sizeof(buffer), CS format, ap))
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "common_error expansion was longer than " SIZE_T_FMT, sizeof(buffer));
+ g = string_vformat(NULL, TRUE, CS format, ap);
va_end(ap);
- addr->message = string_copy(buffer);
+ addr->message = string_from_gstring(g);
}
-for (addr2 = addr->next; addr2; addr2 = addr2->next)
+for (address_item * addr2 = addr->next; addr2; addr2 = addr2->next)
{
addr2->basic_errno = code;
addr2->message = addr->message;
static BOOL
check_never_users(uid_t uid, uid_t *nusers)
{
-int i;
if (!nusers) return FALSE;
-for (i = 1; i <= (int)(nusers[0]); i++) if (nusers[i] == uid) return TRUE;
+for (int i = 1; i <= (int)(nusers[0]); i++) if (nusers[i] == uid) return TRUE;
return FALSE;
}
uschar *new_return_path = expand_string(tp->return_path);
if (!new_return_path)
{
- if (!expand_string_forcedfail)
+ if (!f.expand_string_forcedfail)
{
common_error(TRUE, addr, ERRNO_EXPANDFAIL,
US"Failed to expand return path \"%s\" in %s transport: %s",
DEBUG(D_deliver)
{
- address_item *batched;
debug_printf(" home=%s current=%s\n", deliver_home, working_directory);
- for (batched = addr->next; batched; batched = batched->next)
+ for (address_item * batched = addr->next; batched; batched = batched->next)
debug_printf("additional batched address: %s\n", batched->address);
}
/* In the test harness, wait just a bit to let the subprocess finish off
any debug output etc first. */
- if (running_in_test_harness) millisleep(300);
+ if (f.running_in_test_harness) millisleep(300);
DEBUG(D_deliver) debug_printf("journalling %s", big_buffer);
len = Ustrlen(big_buffer);
struct timeval deliver_time;
address_item *addr2, *addr3, *nextaddr;
int logflags = LOG_MAIN;
- int logchar = dont_deliver? '*' : '=';
+ int logchar = f.dont_deliver? '*' : '=';
transport_instance *tp;
uschar * serialize_key = NULL;
if (!(tp = addr->transport))
{
logflags |= LOG_PANIC;
- disable_logging = FALSE; /* Jic */
+ f.disable_logging = FALSE; /* Jic */
addr->message = addr->router
? string_sprintf("No transport set by %s router", addr->router->name)
: string_sprintf("No transport set by system filter");
/* There are weird cases where logging is disabled */
- disable_logging = tp->disable_logging;
+ f.disable_logging = tp->disable_logging;
/* Check for batched addresses and possible amalgamation. Skip all the work
if either batch_max <= 1 or there aren't any other addresses for local
of these checks, rather than for all local deliveries, because some local
deliveries (e.g. to pipes) can take a substantial time. */
- if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE)))
+ if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE)))
{
DEBUG(D_deliver|D_retry|D_hints_lookup)
debug_printf("no retry data available\n");
retry_record->expired);
}
- if (queue_running && !deliver_force)
+ if (f.queue_running && !f.deliver_force)
{
ok = (now - retry_record->time_stamp > retry_data_expire)
|| (now >= retry_record->next_try)
DEBUG(D_deliver|D_transport)
debug_printf("%s shadow transport returned %s for %s\n",
- stp->name,
- sresult == OK ? "OK" :
- sresult == DEFER ? "DEFER" :
- sresult == FAIL ? "FAIL" :
- sresult == PANIC ? "PANIC" : "?",
- shadow_addr->address);
+ stp->name, rc_to_string(sresult), shadow_addr->address);
}
DEBUG(D_deliver|D_transport)
DEBUG(D_deliver|D_transport)
debug_printf("%s transport returned %s for %s\n",
- tp->name,
- result == OK ? "OK" :
- result == DEFER ? "DEFER" :
- result == FAIL ? "FAIL" :
- result == PANIC ? "PANIC" : "?",
- addr2->address);
+ tp->name, rc_to_string(result), addr2->address);
/* If there is a retry_record, or if delivery is deferred, build a retry
item for setting a new retry time or deleting the old retry record from
DEBUG(D_deliver)
{
- address_item *addr;
debug_printf("remote addresses after sorting:\n");
- for (addr = addr_remote; addr; addr = addr->next)
+ for (address_item * addr = addr_remote; addr; addr = addr->next)
debug_printf(" %s\n", addr->address);
}
}
often bigger) so even if we are reading while the subprocess is still going, we
should never have only a partial item in the buffer.
-hs12: This assumption is not true anymore, since we got quit large items (certificate
-information and such)
+hs12: This assumption is not true anymore, since we get quite large items (certificate
+information and such).
Argument:
poffset the offset of the parlist item
it in with the other info, in order to keep each message short enough to
guarantee it won't be split in the pipe. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
case 'X':
if (!addr) goto ADDR_MISMATCH; /* Below, in 'A' handler */
switch (*subid)
}
while (*ptr++);
break;
-#endif /*SUPPORT_TLS*/
+#endif /*DISABLE_TLS*/
case 'C': /* client authenticator information */
switch (*subid)
break;
#endif
+ case 'L':
+ switch (*subid)
+ {
+#ifdef SUPPORT_PIPE_CONNECT
+ case 2: setflag(addr, af_early_pipe); /*FALLTHROUGH*/
+#endif
+ case 1: setflag(addr, af_pipelining); break;
+ }
+ break;
+
case 'K':
setflag(addr, af_chunking_used);
break;
case 'T':
setflag(addr, af_tcp_fastopen_conn);
if (*subid > '0') setflag(addr, af_tcp_fastopen);
+ if (*subid > '1') setflag(addr, af_tcp_fastopen_data);
break;
case 'D':
remote_post_process(address_item *addr, int logflags, uschar *msg,
BOOL fallback)
{
-host_item *h;
-
/* If any host addresses were found to be unusable, add them to the unusable
tree so that subsequent deliveries don't try them. */
-for (h = addr->host_list; h; h = h->next)
+for (host_item * h = addr->host_list; h; h = h->next)
if (h->address)
if (h->status >= hstatus_unusable) tree_add_unusable(h);
do_remote_deliveries(BOOL fallback)
{
int parmax;
-int delivery_count;
int poffset;
parcount = 0; /* Number of executing subprocesses */
/* Now loop for each remote delivery */
-for (delivery_count = 0; addr_remote; delivery_count++)
+for (int delivery_count = 0; addr_remote; delivery_count++)
{
pid_t pid;
uid_t uid;
if (!(tp = addr->transport))
{
- disable_logging = FALSE; /* Jic */
+ f.disable_logging = FALSE; /* Jic */
panicmsg = US"No transport set by router";
goto panic_continue;
}
uschar *new_return_path = expand_string(tp->return_path);
if (new_return_path)
return_path = new_return_path;
- else if (!expand_string_forcedfail)
+ else if (!f.expand_string_forcedfail)
{
panicmsg = string_sprintf("Failed to expand return path \"%s\": %s",
tp->return_path, expand_string_message);
we must check that the continue host is on the list. Otherwise, the
host is set in the transport. */
- continue_more = FALSE; /* In case got set for the last lot */
+ f.continue_more = FALSE; /* In case got set for the last lot */
if (continue_transport)
{
BOOL ok = Ustrcmp(continue_transport, tp->name) == 0;
&& addr->host_list
)
{
- host_item * h;
ok = FALSE;
- for (h = addr->host_list; h; h = h->next)
+ for (host_item * h = addr->host_list; h; h = h->next)
if (Ustrcmp(h->name, continue_hostname) == 0)
/*XXX should also check port here */
{ ok = TRUE; break; }
connected to is too hard to manage. Perhaps we need a finer-grain
interface to the transport. */
- for (next = addr_remote; next && !continue_more; next = next->next)
- {
- host_item *h;
- for (h = next->host_list; h; h = h->next)
+ for (next = addr_remote; next && !f.continue_more; next = next->next)
+ for (host_item * h = next->host_list; h; h = h->next)
if (Ustrcmp(h->name, continue_hostname) == 0)
- { continue_more = TRUE; break; }
- }
+ { f.continue_more = TRUE; break; }
}
/* The transports set up the process info themselves as they may connect
search_tidyup();
-
if ((pid = fork()) == 0)
{
int fd = pfd[pipe_write];
host_item *h;
- DEBUG(D_deliver) debug_selector |= D_pid; // hs12
/* Setting this global in the subprocess means we need never clear it */
transport_name = tp->name;
/* There are weird circumstances in which logging is disabled */
- disable_logging = tp->disable_logging;
+ f.disable_logging = tp->disable_logging;
/* Show pids on debug output if parallelism possible */
predictable settings for each delivery process, so do something explicit
here rather they rely on the fixed reset in the random number function. */
- random_seed = running_in_test_harness ? 42 + 2*delivery_count : 0;
+ random_seed = f.running_in_test_harness ? 42 + 2*delivery_count : 0;
/* Set close-on-exec on the pipe so that it doesn't get passed on to
a new process that may be forked to do another delivery down the same
#ifdef SUPPORT_DANE
if (tls_out.dane_verified) setflag(addr, af_dane_verified);
#endif
+# ifdef EXPERIMENTAL_TLS_RESUME
+ if (tls_out.resumption & RESUME_USED) setflag(addr, af_tls_resume);
+# endif
/* Use an X item only if there's something to send */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (addr->cipher)
{
ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->cipher) + 1;
rmt_dlv_checked_write(fd, 'X', '4', big_buffer, ptr - big_buffer);
}
# endif
-#endif /*SUPPORT_TLS*/
+#endif /*DISABLE_TLS*/
if (client_authenticator)
{
rmt_dlv_checked_write(fd, 'P', '0', NULL, 0);
#endif
+ if (testflag(addr, af_pipelining))
+#ifdef SUPPORT_PIPE_CONNECT
+ if (testflag(addr, af_early_pipe))
+ rmt_dlv_checked_write(fd, 'L', '2', NULL, 0);
+ else
+#endif
+ rmt_dlv_checked_write(fd, 'L', '1', NULL, 0);
+
if (testflag(addr, af_chunking_used))
rmt_dlv_checked_write(fd, 'K', '0', NULL, 0);
if (testflag(addr, af_tcp_fastopen_conn))
rmt_dlv_checked_write(fd, 'T',
- testflag(addr, af_tcp_fastopen) ? '1' : '0', NULL, 0);
+ testflag(addr, af_tcp_fastopen) ? testflag(addr, af_tcp_fastopen_data)
+ ? '2' : '1' : '0',
+ NULL, 0);
memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware));
rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware));
if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
{
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (cutthrough.is_tls)
tls_close(cutthrough.cctx.tls_ctx, TLS_NO_SHUTDOWN);
#endif
newly created process get going before we create another process. This should
ensure repeatability in the tests. We only need to wait a tad. */
- else if (running_in_test_harness) millisleep(500);
+ else if (f.running_in_test_harness) millisleep(500);
continue;
continue_closedown(void)
{
if (continue_transport)
- {
- transport_instance *t;
- for (t = transports; t; t = t->next)
+ for (transport_instance * t = transports; t; t = t->next)
if (Ustrcmp(t->name, continue_transport) == 0)
{
if (t->info->closedown) (t->info->closedown)(t);
break;
}
- }
return DELIVER_NOT_ATTEMPTED;
}
print_address_error(address_item *addr, FILE *f, uschar *t)
{
int count = Ustrlen(t);
-uschar *s = testflag(addr, af_pass_message)? addr->message : NULL;
+uschar *s = testflag(addr, af_pass_message) ? addr->message : NULL;
if (!s && !(s = addr->user_message))
return;
+/************************************************/
+
+static void
+print_dsn_addr_action(FILE * f, address_item * addr,
+ uschar * action, uschar * status)
+{
+address_item * pa;
+
+if (addr->dsn_orcpt)
+ fprintf(f,"Original-Recipient: %s\n", addr->dsn_orcpt);
+
+for (pa = addr; pa->parent; ) pa = pa->parent;
+fprintf(f, "Action: %s\n"
+ "Final-Recipient: rfc822;%s\n"
+ "Status: %s\n",
+ action, pa->address, status);
+}
+
+
/*************************************************
* Deliver one message *
*************************************************/
it is obtained from a command line (the -M or -q options), and otherwise it is
known to be a valid message id. */
-Ustrcpy(message_id, id);
-deliver_force = forced;
+if (id != message_id)
+ Ustrcpy(message_id, id);
+f.deliver_force = forced;
return_count = 0;
message_size = 0;
can happen, but in the default situation, unless forced, no delivery is
attempted. */
-if (deliver_freeze)
+if (f.deliver_freeze)
{
#ifdef SUPPORT_MOVE_FROZEN_MESSAGES
/* Moving to another directory removes the message from Exim's view. Other
|| auto_thaw <= 0
|| now <= deliver_frozen_at + auto_thaw
)
- && ( !forced || !deliver_force_thaw
- || !admin_user || continue_hostname
+ && ( !forced || !f.deliver_force_thaw
+ || !f.admin_user || continue_hostname
) )
{
(void)close(deliver_datafile);
if (forced)
{
- deliver_manual_thaw = TRUE;
+ f.deliver_manual_thaw = TRUE;
log_write(0, LOG_MAIN, "Unfrozen by forced delivery");
}
else log_write(0, LOG_MAIN, "Unfrozen by auto-thaw");
/* We get here if any of the rules for unfreezing have triggered. */
- deliver_freeze = FALSE;
+ f.deliver_freeze = FALSE;
update_spool = TRUE;
}
ugid.uid_set = ugid.gid_set = TRUE;
}
else
- {
ugid.uid_set = ugid.gid_set = FALSE;
- }
return_path = sender_address;
- enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */
- system_filtering = TRUE;
+ f.enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */
+ f.system_filtering = TRUE;
/* Any error in the filter file causes a delivery to be abandoned. */
/* Reset things. If the filter message is an empty string, which can happen
for a filter "fail" or "freeze" command with no text, reset it to NULL. */
- system_filtering = FALSE;
- enable_dollar_recipients = FALSE;
+ f.system_filtering = FALSE;
+ f.enable_dollar_recipients = FALSE;
if (filter_message && filter_message[0] == 0) filter_message = NULL;
/* Save the values of the system filter variables so that user filters
unset "delivered", which is forced by the "freeze" command to make -bF
work properly. */
- else if (rc == FF_FREEZE && !deliver_manual_thaw)
+ else if (rc == FF_FREEZE && !f.deliver_manual_thaw)
{
- deliver_freeze = TRUE;
+ f.deliver_freeze = TRUE;
deliver_frozen_at = time(NULL);
process_recipients = RECIP_DEFER;
frozen_info = string_sprintf(" by the system filter%s%s",
/* RECIP_DEFER is set when a system filter freezes a message. */
case RECIP_DEFER:
- new->next = addr_defer;
- addr_defer = new;
- break;
+ new->next = addr_defer;
+ addr_defer = new;
+ break;
/* RECIP_FAIL_FILTER is set when a system filter has obeyed a "fail"
command. */
case RECIP_FAIL_FILTER:
- new->message =
- filter_message ? filter_message : US"delivery cancelled";
- setflag(new, af_pass_message);
- goto RECIP_QUEUE_FAILED; /* below */
+ new->message =
+ filter_message ? filter_message : US"delivery cancelled";
+ setflag(new, af_pass_message);
+ goto RECIP_QUEUE_FAILED; /* below */
/* RECIP_FAIL_TIMEOUT is set when a message is frozen, but is older
been logged. */
case RECIP_FAIL_TIMEOUT:
- new->message = US"delivery cancelled; message timed out";
- goto RECIP_QUEUE_FAILED; /* below */
+ new->message = US"delivery cancelled; message timed out";
+ goto RECIP_QUEUE_FAILED; /* below */
/* RECIP_FAIL is set when -Mg has been used. */
case RECIP_FAIL:
- new->message = US"delivery cancelled by administrator";
- /* Fall through */
+ new->message = US"delivery cancelled by administrator";
+ /* Fall through */
/* Common code for the failure cases above. If this is not a bounce
message, put the address on the failed list so that it is used to
The incident has already been logged. */
RECIP_QUEUE_FAILED:
- if (sender_address[0] != 0)
- {
- new->next = addr_failed;
- addr_failed = new;
- }
+ if (sender_address[0])
+ {
+ new->next = addr_failed;
+ addr_failed = new;
+ }
break;
is a bounce message, it will get frozen. */
case RECIP_FAIL_LOOP:
- new->message = US"Too many \"Received\" headers - suspected mail loop";
- post_process_one(new, FAIL, LOG_MAIN, EXIM_DTYPE_ROUTER, 0);
- break;
+ new->message = US"Too many \"Received\" headers - suspected mail loop";
+ post_process_one(new, FAIL, LOG_MAIN, EXIM_DTYPE_ROUTER, 0);
+ break;
/* Value should be RECIP_ACCEPT; take this as the safe default. */
default:
- if (!addr_new) addr_new = new; else addr_last->next = new;
- addr_last = new;
- break;
+ if (!addr_new) addr_new = new; else addr_last->next = new;
+ addr_last = new;
+ break;
}
#ifndef DISABLE_EVENT
- if (process_recipients != RECIP_ACCEPT)
+ if (process_recipients != RECIP_ACCEPT && event_action)
{
uschar * save_local = deliver_localpart;
const uschar * save_domain = deliver_domain;
+ uschar * addr = new->address, * errmsg = NULL;
+ int start, end, dom;
- deliver_localpart = expand_string(
- string_sprintf("${local_part:%s}", new->address));
- deliver_domain = expand_string(
- string_sprintf("${domain:%s}", new->address));
+ if (!parse_extract_address(addr, &errmsg, &start, &end, &dom, TRUE))
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "failed to parse address '%.100s': %s\n", addr, errmsg);
+ else
+ {
+ deliver_localpart =
+ string_copyn(addr+start, dom ? (dom-1) - start : end - start);
+ deliver_domain = dom ? CUS string_copyn(addr+dom, end - dom) : CUS"";
- (void) event_raise(event_action,
- US"msg:fail:internal", new->message);
+ event_raise(event_action, US"msg:fail:internal", new->message);
- deliver_localpart = save_local;
- deliver_domain = save_domain;
+ deliver_localpart = save_local;
+ deliver_domain = save_domain;
+ }
}
#endif
}
DEBUG(D_deliver)
{
- address_item *p;
debug_printf("Delivery address list:\n");
- for (p = addr_new; p; p = p->next)
+ for (address_item * p = addr_new; p; p = p->next)
debug_printf(" %s %s\n", p->address,
p->onetime_parent ? p->onetime_parent : US"");
}
. If new addresses have been generated by the routers, da capo.
*/
-header_rewritten = FALSE; /* No headers rewritten yet */
+f.header_rewritten = FALSE; /* No headers rewritten yet */
while (addr_new) /* Loop until all addresses dealt with */
{
address_item *addr, *parent;
/* Failure to open the retry database is treated the same as if it does
not exist. In both cases, dbm_file is NULL. */
- if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE)))
+ if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE)))
DEBUG(D_deliver|D_retry|D_route|D_hints_lookup)
debug_printf("no retry data available\n");
if ( domain_retry_record
&& now - domain_retry_record->time_stamp > retry_data_expire
)
+ {
+ DEBUG(D_deliver|D_retry)
+ debug_printf("domain retry record present but expired\n");
domain_retry_record = NULL; /* Ignore if too old */
+ }
address_retry_record = dbfn_read(dbm_file, addr->address_retry_key);
if ( address_retry_record
&& now - address_retry_record->time_stamp > retry_data_expire
)
+ {
+ DEBUG(D_deliver|D_retry)
+ debug_printf("address retry record present but expired\n");
address_retry_record = NULL; /* Ignore if too old */
+ }
if (!address_retry_record)
{
address_retry_record = dbfn_read(dbm_file, altkey);
if ( address_retry_record
&& now - address_retry_record->time_stamp > retry_data_expire)
+ {
+ DEBUG(D_deliver|D_retry)
+ debug_printf("address<sender> retry record present but expired\n");
address_retry_record = NULL; /* Ignore if too old */
+ }
}
}
else
DEBUG(D_deliver|D_retry)
{
if (!domain_retry_record)
- debug_printf("no domain retry record\n");
+ debug_printf("no domain retry record\n");
+ else
+ debug_printf("have domain retry record; next_try = now%+d\n",
+ f.running_in_test_harness ? 0 :
+ (int)(domain_retry_record->next_try - now));
+
if (!address_retry_record)
- debug_printf("no address retry record\n");
+ debug_printf("no address retry record\n");
+ else
+ debug_printf("have address retry record; next_try = now%+d\n",
+ f.running_in_test_harness ? 0 :
+ (int)(address_retry_record->next_try - now));
}
/* If we are sending a message down an existing SMTP connection, we must
addr->message = US"reusing SMTP connection skips previous routing defer";
addr->basic_errno = ERRNO_RRETRY;
(void)post_process_one(addr, DEFER, LOG_MAIN, EXIM_DTYPE_ROUTER, 0);
+
+ addr->message = domain_retry_record->text;
+ setflag(addr, af_pass_message);
}
/* If we are in a queue run, defer routing unless there is no retry data or
which keep the retry record fresh, which can lead to us perpetually
deferring messages. */
- else if ( ( queue_running && !deliver_force
+ else if ( ( f.queue_running && !f.deliver_force
|| continue_hostname
)
&& ( ( domain_retry_record
addr->message = US"retry time not reached";
addr->basic_errno = ERRNO_RRETRY;
(void)post_process_one(addr, DEFER, LOG_MAIN, EXIM_DTYPE_ROUTER, 0);
+
+ /* For remote-retry errors (here and just above) that we've not yet
+ hit the rery time, use the error recorded in the retry database
+ as info in the warning message. This lets us send a message even
+ when we're not failing on a fresh attempt. We assume that this
+ info is not sensitive. */
+
+ addr->message = domain_retry_record
+ ? domain_retry_record->text : address_retry_record->text;
+ setflag(addr, af_pass_message);
}
/* The domain is OK for routing. Remember if retry data exists so it
those domains. During queue runs, queue_domains is forced to be unset.
Optimize by skipping this pass through the addresses if nothing is set. */
- if (!deliver_force && queue_domains)
+ if (!f.deliver_force && queue_domains)
{
address_item *okaddr = NULL;
while (addr_route)
DEBUG(D_deliver|D_retry|D_route)
{
- address_item *p;
debug_printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
debug_printf("After routing:\n Local deliveries:\n");
- for (p = addr_local; p; p = p->next)
+ for (address_item * p = addr_local; p; p = p->next)
debug_printf(" %s\n", p->address);
debug_printf(" Remote deliveries:\n");
- for (p = addr_remote; p; p = p->next)
+ for (address_item * p = addr_remote; p; p = p->next)
debug_printf(" %s\n", p->address);
debug_printf(" Failed addresses:\n");
- for (p = addr_failed; p; p = p->next)
+ for (address_item * p = addr_failed; p; p = p->next)
debug_printf(" %s\n", p->address);
debug_printf(" Deferred addresses:\n");
- for (p = addr_defer; p; p = p->next)
+ for (address_item * p = addr_defer; p; p = p->next)
debug_printf(" %s\n", p->address);
}
there is only address to be delivered - if it succeeds the spool write need not
happen. */
-if ( header_rewritten
+if ( f.header_rewritten
&& ( addr_local && (addr_local->next || addr_remote)
|| addr_remote && addr_remote->next
) )
{
/* Panic-dies on error */
(void)spool_write_header(message_id, SW_DELIVERING, NULL);
- header_rewritten = FALSE;
+ f.header_rewritten = FALSE;
}
-/* If there are any deliveries to be and we do not already have the journal
+/* If there are any deliveries to do and we do not already have the journal
file, create it. This is used to record successful deliveries as soon as
possible after each delivery is known to be complete. A file opened with
O_APPEND is used so that several processes can run simultaneously.
that the mode is correct - the group setting doesn't always seem to get
set automatically. */
- if( fchown(journal_fd, exim_uid, exim_gid)
+ if( exim_fchown(journal_fd, exim_uid, exim_gid, fname)
|| fchmod(journal_fd, SPOOL_MODE)
#ifndef O_CLOEXEC
|| fcntl(journal_fd, F_SETFD, fcntl(journal_fd, F_GETFD) | FD_CLOEXEC)
DEBUG(D_deliver|D_transport)
debug_printf(">>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>\n");
do_local_deliveries();
- disable_logging = FALSE;
+ f.disable_logging = FALSE;
}
/* If queue_run_local is set, we do not want to attempt any remote deliveries,
so just queue them all. */
-if (queue_run_local)
+if (f.queue_run_local)
while (addr_remote)
{
address_item *addr = addr_remote;
if (remote_sort_domains) sort_remote_deliveries();
do_remote_deliveries(TRUE);
}
- disable_logging = FALSE;
+ f.disable_logging = FALSE;
}
{
if (addr_defer)
{
- address_item *addr, *nextaddr;
- for (addr = addr_defer; addr; addr = nextaddr)
+ address_item * nextaddr;
+ for (address_item * addr = addr_defer; addr; addr = nextaddr)
{
log_write(0, LOG_MAIN, "** %s mua_wrapper forced failure for deferred "
"delivery", addr->address);
updating of the database if the -N flag is set, which is a debugging thing that
prevents actual delivery. */
-else if (!dont_deliver)
+else if (!f.dont_deliver)
retry_update(&addr_defer, &addr_failed, &addr_succeed);
/* Send DSN for successful messages if requested */
addr_senddsn = NULL;
-for (addr_dsntmp = addr_succeed; addr_dsntmp; addr_dsntmp = addr_dsntmp->next)
+for (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"
"DSN: envid: %s ret: %d\n"
"DSN: Final recipient: %s\n"
"DSN: Remote SMTP server supports DSN: %d\n",
- addr_dsntmp->router ? addr_dsntmp->router->name : US"(unknown)",
- addr_dsntmp->address,
+ a->router ? a->router->name : US"(unknown)",
+ a->address,
sender_address,
- addr_dsntmp->dsn_orcpt ? addr_dsntmp->dsn_orcpt : US"NULL",
- addr_dsntmp->dsn_flags,
+ a->dsn_orcpt ? a->dsn_orcpt : US"NULL",
+ a->dsn_flags,
dsn_envid ? dsn_envid : US"NULL", dsn_ret,
- addr_dsntmp->address,
- addr_dsntmp->dsn_aware
+ a->address,
+ a->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
+ if ( ( a->dsn_aware != dsn_support_yes
+ || a->dsn_flags & rf_dsnlasthop
)
- && addr_dsntmp->dsn_flags & rf_dsnflags
- && addr_dsntmp->dsn_flags & rf_notify_success
+ && a->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;
addr_senddsn = store_get(sizeof(address_item));
- *addr_senddsn = *addr_dsntmp;
+ *addr_senddsn = *a;
addr_senddsn->next = addr_next;
}
else
}
else /* Creation of child succeeded */
{
- FILE *f = fdopen(fd, "wb");
+ FILE * f = fdopen(fd, "wb");
/* header only as required by RFC. only failure DSN needs to honor RET=FULL */
uschar * bound;
transport_ctx tctx = {{0}};
if (errors_reply_to)
fprintf(f, "Reply-To: %s\n", errors_reply_to);
+ moan_write_from(f);
fprintf(f, "Auto-Submitted: auto-generated\n"
- "From: Mail Delivery System <Mailer-Daemon@%s>\n"
"To: %s\n"
"Subject: Delivery Status Notification\n"
"Content-Type: multipart/report; report-type=delivery-status; boundary=%s\n"
"This message was created automatically by mail delivery software.\n"
" ----- The following addresses had successful delivery notifications -----\n",
- qualify_domain_sender, sender_address, bound, bound);
+ sender_address, bound, bound);
- for (addr_dsntmp = addr_senddsn; addr_dsntmp;
- addr_dsntmp = addr_dsntmp->next)
+ for (address_item * a = addr_senddsn; a; a = a->next)
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"
+ a->address,
+ a->dsn_flags & rf_dsnlasthop ? "via non DSN router"
+ : a->dsn_aware == dsn_support_no ? "to non-DSN-aware mailer"
+ : "via non \"Remote SMTP\" router"
);
fprintf(f, "--%s\n"
}
fputc('\n', f);
- for (addr_dsntmp = addr_senddsn;
- addr_dsntmp;
- addr_dsntmp = addr_dsntmp->next)
+ for (address_item * a = addr_senddsn; a; a = a->next)
{
- if (addr_dsntmp->dsn_orcpt)
- fprintf(f,"Original-Recipient: %s\n", addr_dsntmp->dsn_orcpt);
+ host_item * hu;
- fprintf(f, "Action: delivered\n"
- "Final-Recipient: rfc822;%s\n"
- "Status: 2.0.0\n",
- addr_dsntmp->address);
+ print_dsn_addr_action(f, a, US"delivered", US"2.0.0");
- if (addr_dsntmp->host_used && addr_dsntmp->host_used->name)
+ if ((hu = a->host_used) && hu->name)
fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n\n",
- addr_dsntmp->host_used->name);
+ hu->name);
else
fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n",
- (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP");
+ a->dsn_flags & rf_dsnlasthop ? "DSN" : "SMTP");
}
fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound);
/* Write the original email out */
- tctx.u.fd = fileno(f);
+ tctx.u.fd = fd;
tctx.options = topt_add_return_path | 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);
fflush(f);
/* There are weird cases when logging is disabled in the transport. However,
there may not be a transport (address failed by a router). */
- disable_logging = FALSE;
+ f.disable_logging = FALSE;
if (addr_failed->transport)
- disable_logging = addr_failed->transport->disable_logging;
+ f.disable_logging = addr_failed->transport->disable_logging;
DEBUG(D_deliver)
debug_printf("processing failed address %s\n", addr_failed->address);
mark the recipient done. */
if ( addr_failed->prop.ignore_error
- || ( addr_failed->dsn_flags & rf_dsnflags
- && (addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure
- ) )
+ || addr_failed->dsn_flags & (rf_dsnflags & ~rf_notify_failure)
+ )
{
addr = addr_failed;
addr_failed = addr->next;
if (addr->return_filename) Uunlink(addr->return_filename);
+#ifndef DISABLE_EVENT
+ msg_event_raise(US"msg:fail:delivery", addr);
+#endif
log_write(0, LOG_MAIN, "%s%s%s%s: error ignored",
addr->address,
!addr->parent ? US"" : US" <",
int filecount = 0;
int rcount = 0;
uschar *bcc, *emf_text;
- FILE *f = fdopen(fd, "wb");
- FILE *emf = NULL;
+ FILE * fp = fdopen(fd, "wb");
+ FILE * emf = 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;
if (testflag(addr, af_hide_child)) continue;
if (rcount >= 50)
{
- fprintf(f, "\n");
+ fprintf(fp, "\n");
rcount = 0;
}
- fprintf(f, "%s%s",
+ fprintf(fp, "%s%s",
rcount++ == 0
? "X-Failed-Recipients: "
: ",\n ",
? string_printing(addr->parent->address)
: string_printing(addr->address));
}
- if (rcount > 0) fprintf(f, "\n");
+ if (rcount > 0) fprintf(fp, "\n");
/* Output the standard headers */
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);
+ fprintf(fp, "Reply-To: %s\n", errors_reply_to);
+ fprintf(fp, "Auto-Submitted: auto-replied\n");
+ moan_write_from(fp);
+ fprintf(fp, "To: %s\n", bounce_recipient);
/* generate boundary string and output MIME-Headers */
bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
- fprintf(f, "Content-Type: multipart/report;"
+ fprintf(fp, "Content-Type: multipart/report;"
" report-type=delivery-status; boundary=%s\n"
"MIME-Version: 1.0\n",
bound);
/* Quietly copy to configured additional addresses if required. */
if ((bcc = moan_check_errorcopy(bounce_recipient)))
- fprintf(f, "Bcc: %s\n", bcc);
+ fprintf(fp, "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. */
if ((emf_text = next_emf(emf, US"header")))
- fprintf(f, "%s\n", emf_text);
+ fprintf(fp, "%s\n", emf_text);
else
- fprintf(f, "Subject: Mail delivery failed%s\n\n",
+ fprintf(fp, "Subject: Mail delivery failed%s\n\n",
to_sender? ": returning message to sender" : "");
/* output human readable part as text/plain section */
- fprintf(f, "--%s\n"
+ fprintf(fp, "--%s\n"
"Content-type: text/plain; charset=us-ascii\n\n",
bound);
if ((emf_text = next_emf(emf, US"intro")))
- fprintf(f, "%s", CS emf_text);
+ fprintf(fp, "%s", CS emf_text);
else
{
- fprintf(f,
+ fprintf(fp,
/* 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)
- fprintf(f, "%s", CS bounce_message_text);
+ fprintf(fp, "%s", CS bounce_message_text);
if (to_sender)
- fprintf(f,
+ fprintf(fp,
"\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,
+ fprintf(fp,
"\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);
}
- fputc('\n', f);
+ fputc('\n', fp);
/* 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
paddr = &msgchain;
for (addr = msgchain; addr; addr = *paddr)
{
- if (print_address_information(addr, f, US" ", US"\n ", US""))
- print_address_error(addr, f, US"");
+ if (print_address_information(addr, fp, US" ", US"\n ", US""))
+ print_address_error(addr, fp, US"");
/* End the final line for the address */
- fputc('\n', f);
+ fputc('\n', fp);
/* Leave on msgchain if there's a return file. */
}
}
- fputc('\n', f);
+ fputc('\n', fp);
/* Get the next text, whether we need it or not, so as to be
positioned for the one after. */
address_item *nextaddr;
if (emf_text)
- fprintf(f, "%s", CS emf_text);
+ fprintf(fp, "%s", CS emf_text);
else
- fprintf(f,
+ fprintf(fp,
"The following text was generated during the delivery "
"attempt%s:\n", (filecount > 1)? "s" : "");
/* List all the addresses that relate to this file */
- fputc('\n', f);
+ fputc('\n', fp);
while(addr) /* Insurance */
{
- print_address_information(addr, f, US"------ ", US"\n ",
+ print_address_information(addr, fp, US"------ ", US"\n ",
US" ------\n");
if (addr->return_filename) break;
addr = addr->next;
}
- fputc('\n', f);
+ fputc('\n', fp);
/* Now copy the file */
if (!(fm = Ufopen(addr->return_filename, "rb")))
- fprintf(f, " +++ Exim error... failed to open text file: %s\n",
+ fprintf(fp, " +++ Exim error... failed to open text file: %s\n",
strerror(errno));
else
{
- while ((ch = fgetc(fm)) != EOF) fputc(ch, f);
+ while ((ch = fgetc(fm)) != EOF) fputc(ch, fp);
(void)fclose(fm);
}
Uunlink(addr->return_filename);
addr->next = handled_addr;
handled_addr = topaddr;
}
- fputc('\n', f);
+ fputc('\n', fp);
}
/* output machine readable part */
#ifdef SUPPORT_I18N
if (message_smtputf8)
- fprintf(f, "--%s\n"
+ fprintf(fp, "--%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"
+ fprintf(fp, "--%s\n"
"Content-type: message/delivery-status\n\n"
"Reporting-MTA: dns; %s\n",
bound, smtp_active_hostname);
/* 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);
+ fprintf(fp, "Original-Envelope-ID: %s\n", dsn_envid);
else
- fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n");
+ fprintf(fp, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n");
}
- fputc('\n', f);
+ fputc('\n', fp);
for (addr = handled_addr; addr; addr = addr->next)
{
host_item * hu;
- fprintf(f, "Action: failed\n"
- "Final-Recipient: rfc822;%s\n"
- "Status: 5.0.0\n",
- addr->address);
+
+ print_dsn_addr_action(fp, addr, US"failed", US"5.0.0");
+
if ((hu = addr->host_used) && hu->name)
{
- fprintf(f, "Remote-MTA: dns; %s\n", hu->name);
+ fprintf(fp, "Remote-MTA: dns; %s\n", hu->name);
#ifdef EXPERIMENTAL_DSN_INFO
{
const uschar * s;
{
uschar * p = hu->port == 25
? US"" : string_sprintf(":%d", hu->port);
- fprintf(f, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p);
+ fprintf(fp, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p);
}
if ((s = addr->smtp_greeting) && *s)
- fprintf(f, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s);
+ fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s);
if ((s = addr->helo_response) && *s)
- fprintf(f, "X-Remote-MTA-helo-response: X-str; %s\n", s);
+ fprintf(fp, "X-Remote-MTA-helo-response: X-str; %s\n", s);
if ((s = addr->message) && *s)
- fprintf(f, "X-Exim-Diagnostic: X-str; %s\n", s);
+ fprintf(fp, "X-Exim-Diagnostic: X-str; %s\n", s);
}
#endif
- print_dsn_diagnostic_code(addr, f);
+ print_dsn_diagnostic_code(addr, fp);
}
- fputc('\n', f);
+ fputc('\n', fp);
}
/* Now copy the message, trying to give an intelligible comment if
bounce_return_size_limit is always honored.
*/
- fprintf(f, "--%s\n", bound);
+ fprintf(fp, "--%s\n", bound);
dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned";
dsnnotifyhdr = NULL;
if (message_smtputf8)
fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n"
: "Content-type: message/global\n\n",
- f);
+ fp);
else
#endif
fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n"
: "Content-type: message/rfc822\n\n",
- f);
+ fp);
- fflush(f);
+ fflush(fp);
transport_filter_argv = NULL; /* Just in case */
return_path = sender_address; /* In case not previously set */
{ /* Dummy transport for headers add */
transport_ctx tctx = {{0}};
transport_instance tb = {0};
- tctx.u.fd = fileno(f);
+ tctx.u.fd = fileno(fp);
tctx.tblock = &tb;
tctx.options = topt;
tb.add_headers = dsnnotifyhdr;
+ /*XXX no checking for failure! buggy! */
transport_write_message(&tctx, 0);
}
- fflush(f);
+ fflush(fp);
/* we never add the final text. close the file */
if (emf)
(void)fclose(emf);
- fprintf(f, "\n--%s--\n", bound);
+ fprintf(fp, "\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. */
- (void)fclose(f);
+ (void)fclose(fp);
rc = child_close(pid, 0); /* Waits for child to close, no timeout */
/* In the test harness, let the child do it's thing first. */
- if (running_in_test_harness) millisleep(500);
+ if (f.running_in_test_harness) millisleep(500);
/* If the process failed, there was some disaster in setting up the
error message. Unless the message is very old, ensure that addr_defer
if (now - received_time.tv_sec < retry_maximum_timeout && !addr_defer)
{
addr_defer = (address_item *)(+1);
- deliver_freeze = TRUE;
+ f.deliver_freeze = TRUE;
deliver_frozen_at = time(NULL);
/* Panic-dies on error */
(void)spool_write_header(message_id, SW_DELIVERING, NULL);
}
}
-disable_logging = FALSE; /* In case left set */
+f.disable_logging = FALSE; /* In case left set */
/* Come here from the mua_wrapper case if routing goes wrong */
log_write(0, LOG_MAIN, "Completed");
/* Unset deliver_freeze so that we won't try to move the spool files further down */
- deliver_freeze = FALSE;
+ f.deliver_freeze = FALSE;
#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"msg:complete", NULL);
If all the deferred addresses have an error number that indicates "retry time
not reached", skip sending the warning message, because it won't contain the
reason for the delay. It will get sent at the next real delivery attempt.
+ Exception: for retries caused by a remote peer we use the error message
+ store in the retry DB as the reason.
However, if at least one address has tried, we'd better include all of them in
the message.
else if (addr_defer != (address_item *)(+1))
{
- address_item *addr;
uschar *recipients = US"";
- BOOL delivery_attempted = FALSE;
+ BOOL want_warning_msg = FALSE;
deliver_domain = testflag(addr_defer, af_pfr)
? addr_defer->parent->domain : addr_defer->domain;
- for (addr = addr_defer; addr; addr = addr->next)
+ for (address_item * addr = addr_defer; addr; addr = addr->next)
{
address_item *otaddr;
- if (addr->basic_errno > ERRNO_RETRY_BASE) delivery_attempted = TRUE;
+ if (addr->basic_errno > ERRNO_WARN_BASE) want_warning_msg = TRUE;
if (deliver_domain)
{
is not sent. Another attempt will be made at the next delivery attempt (if
it also defers). */
- if ( !queue_2stage
- && delivery_attempted
- && ( ((addr_defer->dsn_flags & rf_dsnflags) == 0)
- || (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay
+ if ( !f.queue_2stage
+ && want_warning_msg
+ && ( !(addr_defer->dsn_flags & rf_dsnflags)
+ || addr_defer->dsn_flags & rf_notify_delay
)
&& delay_warning[1] > 0
&& sender_address[0] != 0
time off the list. In queue runs, the list pointer gets updated in the
calling process. */
- if (running_in_test_harness && fudged_queue_times[0] != 0)
+ if (f.running_in_test_harness && fudged_queue_times[0] != 0)
{
int qt = readconf_readtime(fudged_queue_times, '/', FALSE);
if (qt >= 0)
DEBUG(D_deliver)
{
- debug_printf("time on queue = %s\n", readconf_printtime(queue_time));
+ debug_printf("time on queue = %s id %s addr %s\n", readconf_printtime(queue_time), message_id, addr_defer->address);
debug_printf("warning counts: required %d done %d\n", count,
warning_count);
}
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, "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)
+ host_item * hu;
+
+ print_dsn_addr_action(f, addr_dsndefer, US"delayed", US"4.0.0");
+
+ if ((hu = addr_dsndefer->host_used) && hu->name)
{
- fprintf(f, "Remote-MTA: dns; %s\n",
- addr_dsndefer->host_used->name);
+ fprintf(f, "Remote-MTA: dns; %s\n", hu->name);
print_dsn_diagnostic_code(addr_dsndefer, f);
}
fputc('\n', f);
return_path = sender_address; /* In case not previously set */
/* Write the original email out */
+ /*XXX no checking for failure! buggy! */
transport_write_message(&tctx, 0);
fflush(f);
/* If this was a first delivery attempt, unset the first time flag, and
ensure that the spool gets updated. */
- if (deliver_firsttime)
+ if (f.deliver_firsttime)
{
- deliver_firsttime = FALSE;
+ f.deliver_firsttime = FALSE;
update_spool = TRUE;
}
For the "tell" message, we turn \n back into newline. Also, insert a newline
near the start instead of the ": " string. */
- if (deliver_freeze)
+ if (f.deliver_freeze)
{
- if (freeze_tell && freeze_tell[0] != 0 && !local_error_message)
+ if (freeze_tell && freeze_tell[0] != 0 && !f.local_error_message)
{
uschar *s = string_copy(frozen_info);
uschar *ss = Ustrstr(s, " by the system filter: ");
DEBUG(D_deliver)
debug_printf("delivery deferred: update_spool=%d header_rewritten=%d\n",
- update_spool, header_rewritten);
+ update_spool, f.header_rewritten);
- if (update_spool || header_rewritten)
+ if (update_spool || f.header_rewritten)
/* Panic-dies on error */
(void)spool_write_header(message_id, SW_DELIVERING, NULL);
}
/* Move the message off the spool if requested */
#ifdef SUPPORT_MOVE_FROZEN_MESSAGES
- if (deliver_freeze && move_frozen_messages)
+ if (f.deliver_freeze && move_frozen_messages)
(void)spool_move_message(id, message_subdir, US"", US"F");
#endif
}
#ifdef EXIM_TFO_PROBE
tfo_probe();
#else
-tcp_fastopen_ok = TRUE;
+f.tcp_fastopen_ok = TRUE;
#endif
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);
+ regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (!regex_STARTTLS) regex_STARTTLS =
regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
-
-# ifdef EXPERIMENTAL_REQUIRETLS
-if (!regex_REQUIRETLS) regex_REQUIRETLS =
- regex_must_compile(US"\\n250[\\s\\-]REQUIRETLS(\\s|\\n|$)", FALSE, TRUE);
-# endif
#endif
if (!regex_CHUNKING) regex_CHUNKING =
if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+
+#ifdef SUPPORT_PIPE_CONNECT
+if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
+ regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
+#endif
}
int rc;
uschar * new_sender_address,
* save_sender_address;
-BOOL save_qr = queue_running;
+BOOL save_qr = f.queue_running;
uschar * spoolname;
/* make spool_open_datafile non-noisy on fail */
-queue_running = TRUE;
+f.queue_running = TRUE;
/* Side effect: message_subdir is set for the (possibly split) spool directory */
deliver_datafile = spool_open_datafile(id);
-queue_running = save_qr;
+f.queue_running = save_qr;
if (deliver_datafile < 0)
return NULL;
smtp_peer_options = cutthrough.peer_options;
continue_sequence = 0;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (cutthrough.is_tls)
{
int pfd[2], pid;
else if (pid == 0) /* child: fork again to totally disconnect */
{
- if (running_in_test_harness) millisleep(100); /* let parent debug out */
+ if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
/* does not return */
smtp_proxy_tls(cutthrough.cctx.tls_ctx, big_buffer, big_buffer_size,
pfd, 5*60);
}
return; /* compiler quietening; control does not reach here. */
+#ifndef DISABLE_TLS
fail:
log_write(0,
LOG_MAIN | (exec_type == CEE_EXEC_EXIT ? LOG_PANIC : LOG_PANIC_DIE),
Note: this must be _exit(), not exit(). */
_exit(EX_EXECFAILED);
+#endif
}
/* vi: aw ai sw=2