Returns: One of the END_xxx values indicating why it stopped reading
*/
-/*XXX cutthrough - need to copy to destination, not including the
- terminating dot, canonicalizing newlines.
-*/
static int
read_message_data_smtp(FILE *fout)
header_line *h, *next;
header_line *last_received = NULL;
+if (acl_removed_headers != NULL)
+ {
+ DEBUG(D_receive|D_acl) debug_printf(">>Headers removed by %s ACL:\n", acl_name);
+
+ for (h = header_list; h != NULL; h = h->next)
+ {
+ uschar *list;
+ BOOL include_header;
+
+ if (h->type == htype_old) continue;
+
+ include_header = TRUE;
+ list = acl_removed_headers;
+
+ int sep = ':'; /* This is specified as a colon-separated list */
+ uschar *s;
+ uschar buffer[128];
+ while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))
+ != NULL)
+ {
+ int len = Ustrlen(s);
+ if (header_testname(h, s, len, FALSE))
+ {
+ h->type = htype_old;
+ DEBUG(D_receive|D_acl) debug_printf(" %s", h->text);
+ }
+ }
+ }
+ acl_removed_headers = NULL;
+ DEBUG(D_receive|D_acl) debug_printf(">>\n");
+ }
+
if (acl_added_headers == NULL) return;
DEBUG(D_receive|D_acl) debug_printf(">>Headers added by %s ACL:\n", acl_name);
uschar *resent_prefix = US"";
uschar *blackholed_by = NULL;
uschar *blackhole_log_msg = US"";
+int cutthrough_done = 0;
flock_t lock_data;
error_block *bad_addresses = NULL;
/* Extracting the recipient list from an input file is incompatible with
cutthrough delivery with the no-spool option. It shouldn't be possible
- to set up the combination, but just in case kill any ongoing connection. */
-/*XXX add no-spool */
+to set up the combination, but just in case kill any ongoing connection. */
if (extract_recip || !smtp_input)
- cancel_cutthrough_connection();
+ cancel_cutthrough_connection("not smtp input");
/* Initialize the chain of headers by setting up a place-holder for Received:
header. Temporarily mark it as "old", i.e. not to be used. We keep header_last
case htype_received:
h->type = htype_received;
-/*XXX cutthrough delivery - need to error on excessive number here */
received_count++;
break;
return message_ended == END_DOT;
}
-/*XXX cutthrough deliver:
+/* Cutthrough delivery:
We have to create the Received header now rather than at the end of reception,
so the timestamp behaviour is a change to the normal case.
XXX Ensure this gets documented XXX.
+ Having created it, send the headers to the destination.
*/
if (cutthrough_fd >= 0)
{
+ if (received_count > received_headers_max)
+ {
+ cancel_cutthrough_connection("too many headers");
+ if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */
+ log_write(0, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: "
+ "Too many \"Received\" headers",
+ sender_address,
+ (sender_fullhost == NULL)? "" : " H=",
+ (sender_fullhost == NULL)? US"" : sender_fullhost,
+ (sender_ident == NULL)? "" : " U=",
+ (sender_ident == NULL)? US"" : sender_ident);
+ message_id[0] = 0; /* Indicate no message accepted */
+ smtp_reply = US"550 Too many \"Received\" headers - suspected mail loop";
+ goto TIDYUP; /* Skip to end of function */
+ }
received_header_gen();
add_acl_headers(US"MAIL or RCPT");
(void) cutthrough_headers_send();
}
-
-
-/*XXX cutthrough deliver:
- Here's where we open the data spoolfile. Want to optionally avoid.
-*/
+
/* Open a new spool file for the data portion of the message. We need
to access it both via a file descriptor and a stream. Try to make the
{
uschar *s = next->text;
int len = next->slen;
- /*XXX cutthrough - writing the data spool file here. Want to optionally avoid. */
(void)fwrite(s, 1, len, data_file);
body_linecount++; /* Assumes only 1 line */
}
(indicated by '.'), or might have encountered an error while writing the
message id or "next" line. */
-/* XXX cutthrough - no-spool option....... */
if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT)
{
if (smtp_input)
{
- /*XXX cutthrough - writing the data spool file here. Want to optionally avoid. */
- /* Would suffice to leave data_file arg NULL */
message_ended = read_message_data_smtp(data_file);
receive_linecount++; /* The terminating "." line */
}
if (smtp_input && message_ended == END_EOF)
{
Uunlink(spool_name); /* Lose data file when closed */
- cancel_cutthrough_connection();
+ cancel_cutthrough_connection("sender closed connection");
message_id[0] = 0; /* Indicate no message accepted */
smtp_reply = handle_lost_connection(US"");
smtp_yield = FALSE;
if (message_ended == END_SIZE)
{
Uunlink(spool_name); /* Lose the data file when closed */
- cancel_cutthrough_connection();
+ cancel_cutthrough_connection("mail too big");
if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */
log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: "
the input in cases of output errors, since the far end doesn't expect to see
anything until the terminating dot line is sent. */
-/* XXX cutthrough - no-spool option....... */
if (fflush(data_file) == EOF || ferror(data_file) ||
EXIMfsync(fileno(data_file)) < 0 || (receive_ferror)())
{
log_write(0, LOG_MAIN, "Message abandoned: %s", msg);
Uunlink(spool_name); /* Lose the data file */
- cancel_cutthrough_connection();
+ cancel_cutthrough_connection("error writing spoolfile");
if (smtp_input)
{
/* No I/O errors were encountered while writing the data file. */
-/*XXX cutthrough - avoid message if no-spool option */
DEBUG(D_receive) debug_printf("Data file written for message %s\n", message_id);
exit. (This can't be SMTP, which always ensures there's at least one
syntactically good recipient address.) */
-/*XXX cutthrough - can't if no-spool option. extract_recip is a fn arg.
- Make incompat with no-spool at fn start. */
-
if (extract_recip && (bad_addresses != NULL || recipients_count == 0))
{
DEBUG(D_receive)
add_acl_headers(US"MAIL or RCPT");
}
-else if (data_fd >= 0)
+else
message_body_size = (fstat(data_fd, &statbuf) == 0)?
statbuf.st_size - SPOOL_DATA_START_OFFSET : -1;
-else
- /*XXX cutthrough - XXX how to get the body size? */
- /* perhaps a header-size to subtract from message_size? */
- message_body_size = message_size - 1;
/* If an ACL is specified for checking things at this stage of reception of a
message, run it, unless all the recipients were removed by "discard" in earlier
#ifndef DISABLE_DKIM
if (!dkim_disable_verify)
{
-/* XXX cutthrough - no-spool option....... */
/* Finish verification, this will log individual signature results to
the mainlog */
dkim_exim_verify_finish();
{
DEBUG(D_receive)
debug_printf("acl_smtp_dkim: acl_check returned %d on %s, skipping remaining items\n", rc, item);
- cancel_cutthrough_connection();
+ cancel_cutthrough_connection("dkim acl not ok");
break;
}
}
#endif /* DISABLE_DKIM */
#ifdef WITH_CONTENT_SCAN
-/* XXX cutthrough - no-spool option....... */
if (recipients_count > 0 &&
acl_smtp_mime != NULL &&
!run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by))
/* Check the recipients count again, as the MIME ACL might have changed
them. */
-/* XXX cutthrough - no-spool option must document that data-acl has no file access */
-/* but can peek at headers */
-
if (acl_smtp_data != NULL && recipients_count > 0)
{
rc = acl_check(ACL_WHERE_DATA, NULL, acl_smtp_data, &user_msg, &log_msg);
blackholed_by = US"DATA ACL";
if (log_msg != NULL)
blackhole_log_msg = string_sprintf(": %s", log_msg);
- cancel_cutthrough_connection();
+ cancel_cutthrough_connection("data acl discard");
}
else if (rc != OK)
{
Uunlink(spool_name);
- cancel_cutthrough_connection();
+ cancel_cutthrough_connection("data acl not ok");
#ifdef WITH_CONTENT_SCAN
unspool_mbox();
#endif
os_non_restarting_signal(SIGALRM, local_scan_timeout_handler);
if (local_scan_timeout > 0) alarm(local_scan_timeout);
-/* XXX cutthrough - no-spool option..... */
rc = local_scan(data_fd, &local_scan_data);
alarm(0);
os_non_restarting_signal(SIGALRM, sigalrm_handler);
#ifdef EXPERIMENTAL_BRIGHTMAIL
if (bmi_run == 1) {
/* rewind data file */
- /* XXX cutthrough - no-spool option..... */
lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
bmi_verdicts = bmi_process_message(header_list, data_fd);
};
else
{
- /*XXX cutthrough -
- Optionally want to avoid writing spool files (when no data-time filtering needed) */
-
if ((msg_size = spool_write_header(message_id, SW_RECEIVING, &errmsg)) < 0)
{
log_write(0, LOG_MAIN, "Message abandoned: %s", errmsg);
s = add_host_info_for_log(s, &size, &sptr);
#ifdef SUPPORT_TLS
-if ((log_extra_selector & LX_tls_cipher) != 0 && tls_cipher != NULL)
- s = string_append(s, &size, &sptr, 2, US" X=", tls_cipher);
+if ((log_extra_selector & LX_tls_cipher) != 0 && tls_in.cipher != NULL)
+ s = string_append(s, &size, &sptr, 2, US" X=", tls_in.cipher);
if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
- tls_cipher != NULL)
+ tls_in.cipher != NULL)
s = string_append(s, &size, &sptr, 2, US" CV=",
- tls_certificate_verified? "yes":"no");
-if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
+ tls_in.certificate_verified? "yes":"no");
+if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_in.peerdn != NULL)
s = string_append(s, &size, &sptr, 3, US" DN=\"",
- string_printing(tls_peerdn), US"\"");
-if ((log_extra_selector & LX_tls_sni) != 0 && tls_sni != NULL)
+ string_printing(tls_in.peerdn), US"\"");
+if ((log_extra_selector & LX_tls_sni) != 0 && tls_in.sni != NULL)
s = string_append(s, &size, &sptr, 3, US" SNI=\"",
- string_printing(tls_sni), US"\"");
+ string_printing(tls_in.sni), US"\"");
#endif
if (sender_host_authenticated != NULL)
{
s = string_append(s, &size, &sptr, 2, US" A=", sender_host_authenticated);
if (authenticated_id != NULL)
+ {
s = string_append(s, &size, &sptr, 2, US":", authenticated_id);
+ if (log_extra_selector & LX_smtp_mailauth && authenticated_sender != NULL)
+ s = string_append(s, &size, &sptr, 2, US":", authenticated_sender);
+ }
}
sprintf(CS big_buffer, "%d", msg_size);
s = string_append(s, &size, &sptr, 2, US" S=", big_buffer);
+/* log 8BITMIME mode announced in MAIL_FROM
+ 0 ... no BODY= used
+ 7 ... 7BIT
+ 8 ... 8BITMIME */
+if (log_extra_selector & LX_8bitmime)
+ {
+ sprintf(CS big_buffer, "%d", body_8bitmime);
+ s = string_append(s, &size, &sptr, 2, US" M8S=", big_buffer);
+ }
+
/* If an addr-spec in a message-id contains a quoted string, it can contain
any characters except " \ and CR and so in particular it can contain NL!
Therefore, make sure we use a printing-characters only version for the log.
/* The connection has not gone away; we really are going to take responsibility
for this message. */
-/*XXX cutthrough - had sender last-dot; assume we've sent or bufferred all
+/* Cutthrough - had sender last-dot; assume we've sent (or bufferred) all
data onward by now.
- Send dot onward. If accepted, can the spooled files, log as delivered and accept
+ Send dot onward. If accepted, wipe the spooled files, log as delivered and accept
the sender's dot (below).
- If not accepted: copy response to sender, can the spooled files, log approriately.
+ If rejected: copy response to sender, wipe the spooled files, log approriately.
+ If temp-reject: accept to sender, keep the spooled files.
Having the normal spool files lets us do data-filtering, and store/forward on temp-reject.
+
+ XXX We do not handle queue-only, freezing, or blackholes.
*/
+cutthrough_done = 0;
if(cutthrough_fd >= 0)
{
uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the messsage */
+ /* Logging was done in finaldot() */
switch(msg[0])
- {
- case '2': /* Accept. Do the same to the source; dump any spoolfiles. */
- /* logging was done in finaldot() */
- if(data_file != NULL)
- {
- sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory,
- message_subdir, message_id);
- Uunlink(spool_name);
- sprintf(CS spool_name, "%s/input/%s/%s-H", spool_directory,
- message_subdir, message_id);
- Uunlink(spool_name);
- sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory,
- message_subdir, message_id);
- Uunlink(spool_name);
- }
- break;
-
- default: /* Unknown response, or error. Treat as temp-reject. */
- case '4': /* Temp-reject. If we wrote spoolfiles, keep them and accept. */
- /* If not, temp-reject the source. */
- /*XXX could we mark the spoolfile queue-only or already-tried? */
- log_write(0, LOG_MAIN, "cutthrough target temp-reject: %s", msg);
- if(data_file == NULL)
- smtp_reply= msg; /* Pass on the exact error */
- break;
-
- case '5': /* Perm-reject. Do the same to the source. Dump any spoolfiles */
- log_write(0, LOG_MAIN, "cutthrough target perm-reject: %s", msg);
- smtp_reply= msg; /* Pass on the exact error */
- if(data_file != NULL)
- {
- sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory,
- message_subdir, message_id);
- Uunlink(spool_name);
- sprintf(CS spool_name, "%s/input/%s/%s-H", spool_directory,
- message_subdir, message_id);
- Uunlink(spool_name);
- sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory,
- message_subdir, message_id);
- Uunlink(spool_name);
- }
- break;
- }
+ {
+ case '2': /* Accept. Do the same to the source; dump any spoolfiles. */
+ cutthrough_done = 3;
+ break; /* message_id needed for SMTP accept below */
+
+ default: /* Unknown response, or error. Treat as temp-reject. */
+ case '4': /* Temp-reject. Keep spoolfiles and accept. */
+ cutthrough_done = 1; /* Avoid the usual immediate delivery attempt */
+ break; /* message_id needed for SMTP accept below */
+
+ case '5': /* Perm-reject. Do the same to the source. Dump any spoolfiles */
+ smtp_reply= msg; /* Pass on the exact error */
+ cutthrough_done = 2;
+ break;
+ }
}
if(smtp_reply == NULL)
if (!smtp_batched_input)
{
-/*XXX cutthrough - here's where the originating sender gets given the data-acceptance */
if (smtp_reply == NULL)
{
if (fake_response != OK)
smtp_printf("%.1024s\r\n", smtp_reply);
}
- if (cutthrough_delivery)
- {
- log_write(0, LOG_MAIN, "Completed");
- message_id[0] = 0; /* Prevent a delivery from starting */
+ switch (cutthrough_done)
+ {
+ case 3: log_write(0, LOG_MAIN, "Completed"); /* Delivery was done */
+ case 2: { /* Delete spool files */
+ sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory,
+ message_subdir, message_id);
+ Uunlink(spool_name);
+ sprintf(CS spool_name, "%s/input/%s/%s-H", spool_directory,
+ message_subdir, message_id);
+ Uunlink(spool_name);
+ sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory,
+ message_subdir, message_id);
+ Uunlink(spool_name);
+ }
+ case 1: message_id[0] = 0; /* Prevent a delivery from starting */
+ default:break;
}
+ cutthrough_delivery = FALSE;
}
/* For batched SMTP, generate an error message on failure, and do