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;
}
{
DEBUG(D_deliver)
debug_printf("Event(%s): event_action returned \"%s\"\n", event, s);
+ errno = ERRNO_EVENT;
return s;
}
}
&& 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);
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 */
}
}
- if (LOGGING(pipelining) && testflag(addr, af_pipelining))
- g = string_catn(g, US" L", 2);
+ if (LOGGING(pipelining))
+ {
+ if (testflag(addr, af_pipelining))
+ g = string_catn(g, US" L", 2);
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+ if (testflag(addr, af_early_pipe))
+ g = string_catn(g, US"*", 1);
+#endif
+ }
#ifndef DISABLE_PRDR
if (testflag(addr, af_prdr_used))
&& (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);
}
}
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;
}
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);
}
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);
}
}
case 'L':
switch (*subid)
{
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+ case 2: setflag(addr, af_early_pipe); /*FALLTHROUGH*/
+#endif
case 1: setflag(addr, af_pipelining); break;
}
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;
&& 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; }
interface to the transport. */
for (next = addr_remote; next && !f.continue_more; next = next->next)
- {
- host_item *h;
- for (h = next->host_list; h; h = h->next)
+ for (host_item * h = next->host_list; h; h = h->next)
if (Ustrcmp(h->name, continue_hostname) == 0)
{ f.continue_more = TRUE; break; }
- }
}
/* The transports set up the process info themselves as they may connect
#endif
if (testflag(addr, af_pipelining))
+#ifdef EXPERIMENTAL_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))
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));
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;
ugid.uid_set = ugid.gid_set = TRUE;
}
else
- {
ugid.uid_set = ugid.gid_set = FALSE;
- }
return_path = sender_address;
f.enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */
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 ( 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
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
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);
}
}
-/* 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)
{
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);
}
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}};
/* 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);
tctx.options = topt;
tb.add_headers = dsnnotifyhdr;
+ /*XXX no checking for failure! buggy! */
transport_write_message(&tctx, 0);
}
fflush(fp);
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)
{
it also defers). */
if ( !f.queue_2stage
- && delivery_attempted
+ && want_warning_msg
&& ( !(addr_defer->dsn_flags & rf_dsnflags)
|| addr_defer->dsn_flags & rf_notify_delay
)
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);
}
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);
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
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 EXPERIMENTAL_PIPE_CONNECT
+if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
+ regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
+#endif
}