* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2017 */
/* See the file NOTICE for conditions of use and distribution. */
/* Code for receiving a message and setting up spool files. */
static int data_fd = -1;
static uschar *spool_name = US"";
+enum CH_STATE {LF_SEEN, MID_LINE, CR_SEEN};
/*************************************************
changing the pointer variables.) */
int
-stdin_getc(void)
+stdin_getc(unsigned lim)
{
return getc(stdin);
}
{
uschar *qnewsender;
if (trusted_caller) return TRUE;
-if (newsender == NULL || untrusted_set_sender == NULL) return FALSE;
-qnewsender = (Ustrchr(newsender, '@') != NULL)?
- newsender : string_sprintf("%s@%s", newsender, qualify_domain_sender);
-return
- match_address_list(qnewsender, TRUE, TRUE, CUSS &untrusted_set_sender, NULL, -1,
- 0, NULL) == OK;
+if (!newsender || !untrusted_set_sender) return FALSE;
+qnewsender = Ustrchr(newsender, '@')
+ ? newsender : string_sprintf("%s@%s", newsender, qualify_domain_sender);
+return match_address_list_basic(qnewsender, CUSS &untrusted_set_sender, 0) == OK;
}
{
#ifdef HAVE_STATFS
struct STATVFS statbuf;
+struct stat dummy;
uschar *path;
uschar *name;
uschar buffer[1024];
memset(&statbuf, 0, sizeof(statbuf));
if (STATVFS(CS path, &statbuf) != 0)
- {
- log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat "
- "%s directory %s: %s", name, spool_directory, strerror(errno));
- smtp_closedown(US"spool or log directory problem");
- exim_exit(EXIT_FAILURE);
- }
+ if (stat(CS path, &dummy) == -1 && errno == ENOENT)
+ { /* Can happen on first run after installation */
+ *inodeptr = -1;
+ return -1;
+ }
+ else
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat "
+ "%s directory %s: %s", name, path, strerror(errno));
+ smtp_closedown(US"spool or log directory problem");
+ exim_exit(EXIT_FAILURE);
+ }
*inodeptr = (statbuf.F_FILES > 0)? statbuf.F_FAVAIL : -1;
return (int)(((double)statbuf.F_BAVAIL * (double)statbuf.F_FRSIZE)/1024.0);
+#else
/* Unable to find partition sizes in this environment. */
-#else
*inodeptr = -1;
return -1;
#endif
{
register int last_ch = '\n';
- for (; (ch = (receive_getc)()) != EOF; last_ch = ch)
+ for (; (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF; last_ch = ch)
{
if (ch == 0) body_zerocount++;
if (last_ch == '\r' && ch != '\n')
ch_state = 1;
-while ((ch = (receive_getc)()) != EOF)
+while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
{
if (ch == 0) body_zerocount++;
switch (ch_state)
int ch;
int linelength = 0;
-while ((ch = (receive_getc)()) != EOF)
+while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
{
if (ch == 0) body_zerocount++;
switch (ch_state)
{
message_size++;
if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR;
- (void) cutthrough_put_nl();
+ cutthrough_data_put_nl();
if (ch != '\r') ch_state = 1; else continue;
}
break;
if (ch == '.')
{
uschar c= ch;
- (void) cutthrough_puts(&c, 1);
+ cutthrough_data_puts(&c, 1);
}
ch_state = 1;
break;
message_size++;
body_linecount++;
if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR;
- (void) cutthrough_put_nl();
+ cutthrough_data_put_nl();
if (ch == '\r')
{
ch_state = 2;
if (message_size > thismessage_size_limit) return END_SIZE;
}
if(ch == '\n')
- (void) cutthrough_put_nl();
+ cutthrough_data_put_nl();
else
{
uschar c = ch;
- (void) cutthrough_puts(&c, 1);
+ cutthrough_data_puts(&c, 1);
}
}
/* Variant of the above read_message_data_smtp() specialised for RFC 3030
-CHUNKING. We assume that the incoming has proper CRLF, so only have to scan
-for and strip CR. On the downside there are more protocol reasons to stop.
+CHUNKING. Accept input lines separated by either CRLF or CR or LF and write
+LF-delimited spoolfile. Until we have wireformat spoolfiles, we need the
+body_linecount accounting for proper re-expansion for the wire, so use
+a cut-down version of the state-machine above; we don't need to do leading-dot
+detection and unstuffing.
Arguments:
- fout a FILE to which to write the message; NULL if skipping
+ fout a FILE to which to write the message; NULL if skipping;
+ must be open for both writing and reading.
Returns: One of the END_xxx values indicating why it stopped reading
*/
static int
read_message_bdat_smtp(FILE *fout)
{
+int linelength = 0, ch;
+enum CH_STATE ch_state = LF_SEEN;
+BOOL fix_nl = FALSE;
+
+for(;;)
+ {
+ switch ((ch = (bdat_getc)(GETC_BUFFER_UNLIMITED)))
+ {
+ case EOF: return END_EOF;
+ case ERR: return END_PROTOCOL;
+ case EOD:
+ /* Nothing to get from the sender anymore. We check the last
+ character written to the spool.
+
+ RFC 3030 states, that BDAT chunks are normal text, terminated by CRLF.
+ If we would be strict, we would refuse such broken messages.
+ But we are liberal, so we fix it. It would be easy just to append
+ the "\n" to the spool.
+
+ But there are some more things (line counting, message size calculation and such),
+ that would need to be duplicated here. So we simply do some ungetc
+ trickery.
+ */
+ if (fout)
+ {
+ if (fseek(fout, -1, SEEK_CUR) < 0) return END_PROTOCOL;
+ if (fgetc(fout) == '\n') return END_DOT;
+ }
+
+ if (linelength == -1) /* \r already seen (see below) */
+ {
+ DEBUG(D_receive) debug_printf("Add missing LF\n");
+ bdat_ungetc('\n');
+ continue;
+ }
+ DEBUG(D_receive) debug_printf("Add missing CRLF\n");
+ bdat_ungetc('\r'); /* not even \r was seen */
+ fix_nl = TRUE;
+
+ continue;
+ case '\0': body_zerocount++; break;
+ }
+ switch (ch_state)
+ {
+ case LF_SEEN: /* After LF or CRLF */
+ ch_state = MID_LINE;
+ /* fall through to handle as normal uschar. */
+
+ case MID_LINE: /* Mid-line state */
+ if (ch == '\n')
+ {
+ ch_state = LF_SEEN;
+ body_linecount++;
+ if (linelength > max_received_linelength)
+ max_received_linelength = linelength;
+ linelength = -1;
+ }
+ else if (ch == '\r')
+ {
+ ch_state = CR_SEEN;
+ if (fix_nl) bdat_ungetc('\n');
+ continue; /* don't write CR */
+ }
+ break;
+
+ case CR_SEEN: /* After (unwritten) CR */
+ body_linecount++;
+ if (linelength > max_received_linelength)
+ max_received_linelength = linelength;
+ linelength = -1;
+ if (ch == '\n')
+ ch_state = LF_SEEN;
+ else
+ {
+ message_size++;
+ if (fout && fputc('\n', fout) == EOF) return END_WERROR;
+ cutthrough_data_put_nl();
+ if (ch == '\r') continue; /* don't write CR */
+ ch_state = MID_LINE;
+ }
+ break;
+ }
+
+ /* Add the character to the spool file, unless skipping */
+
+ message_size++;
+ linelength++;
+ if (fout)
+ {
+ if (fputc(ch, fout) == EOF) return END_WERROR;
+ if (message_size > thismessage_size_limit) return END_SIZE;
+ }
+ if(ch == '\n')
+ cutthrough_data_put_nl();
+ else
+ {
+ uschar c = ch;
+ cutthrough_data_puts(&c, 1);
+ }
+ }
+/*NOTREACHED*/
+}
+
+static int
+read_message_bdat_smtp_wire(FILE *fout)
+{
int ch;
-int linelength = 0;
-for (;;) switch (ch = bdat_getc())
+/* Remember that this message uses wireformat. */
+
+DEBUG(D_receive) debug_printf("CHUNKING: writing spoolfile in wire format\n");
+spool_file_wireformat = TRUE;
+
+/* Unfortunately cannot use sendfile() even if not TLS
+as that requires (on linux) mmap-like operations on the input fd.
+
+XXX but worthwhile doing a block interface to the bdat_getc buffer
+in the future */
+
+for (;;) switch (ch = bdat_getc(GETC_BUFFER_UNLIMITED))
{
case EOF: return END_EOF;
case EOD: return END_DOT;
case ERR: return END_PROTOCOL;
- case '\r':
- body_linecount++;
- if (linelength > max_received_linelength)
- max_received_linelength = linelength;
- linelength = -1;
- break;
-
- case 0:
- body_zerocount++;
- /*FALLTHROUGH*/
default:
message_size++;
- linelength++;
+/*XXX not done:
+linelength
+max_received_linelength
+body_linecount
+body_zerocount
+*/
if (fout)
{
if (fputc(ch, fout) == EOF) return END_WERROR;
if (message_size > thismessage_size_limit) return END_SIZE;
}
-#ifdef notyet
- if(ch == '\n')
- (void) cutthrough_put_nl();
- else
- {
- uschar c = ch;
- (void) cutthrough_puts(&c, 1);
- }
-#endif
break;
}
/*NOTREACHED*/
{
log_write(L_lost_incoming_connection | L_smtp_connection, LOG_MAIN,
"%s lost while reading message data%s", smtp_get_connection_info(), s);
+smtp_notquit_exit(US"connection-lost", NULL, NULL);
return US"421 Lost incoming connection";
}
case ACL_WHERE_DKIM:
case ACL_WHERE_MIME:
case ACL_WHERE_DATA:
- if (cutthrough.fd >= 0 && (acl_removed_headers || acl_added_headers))
+ if ( cutthrough.fd >= 0 && cutthrough.delivery
+ && (acl_removed_headers || acl_added_headers))
{
log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs"
" will not take effect on cutthrough deliveries");
}
}
-if (acl_removed_headers != NULL)
+if (acl_removed_headers)
{
- DEBUG(D_receive|D_acl) debug_printf(">>Headers removed by %s ACL:\n", acl_name);
+ DEBUG(D_receive|D_acl) debug_printf_indent(">>Headers removed by %s ACL:\n", acl_name);
- for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old)
+ for (h = header_list; h; h = h->next) if (h->type != htype_old)
{
const uschar * list = acl_removed_headers;
int sep = ':'; /* This is specified as a colon-separated list */
if (header_testname(h, s, Ustrlen(s), FALSE))
{
h->type = htype_old;
- DEBUG(D_receive|D_acl) debug_printf(" %s", h->text);
+ DEBUG(D_receive|D_acl) debug_printf_indent(" %s", h->text);
}
}
acl_removed_headers = NULL;
- DEBUG(D_receive|D_acl) debug_printf(">>\n");
+ DEBUG(D_receive|D_acl) debug_printf_indent(">>\n");
}
-if (acl_added_headers == NULL) return;
-DEBUG(D_receive|D_acl) debug_printf(">>Headers added by %s ACL:\n", acl_name);
+if (!acl_added_headers) return;
+DEBUG(D_receive|D_acl) debug_printf_indent(">>Headers added by %s ACL:\n", acl_name);
-for (h = acl_added_headers; h != NULL; h = next)
+for (h = acl_added_headers; h; h = next)
{
next = h->next;
case htype_add_top:
h->next = header_list;
header_list = h;
- DEBUG(D_receive|D_acl) debug_printf(" (at top)");
+ DEBUG(D_receive|D_acl) debug_printf_indent(" (at top)");
break;
case htype_add_rec:
}
h->next = last_received->next;
last_received->next = h;
- DEBUG(D_receive|D_acl) debug_printf(" (after Received:)");
+ DEBUG(D_receive|D_acl) debug_printf_indent(" (after Received:)");
break;
case htype_add_rfc:
of all headers. Our current header must follow it. */
h->next = last_received->next;
last_received->next = h;
- DEBUG(D_receive|D_acl) debug_printf(" (before any non-Received: or Resent-*: header)");
+ DEBUG(D_receive|D_acl) debug_printf_indent(" (before any non-Received: or Resent-*: header)");
break;
default:
h->type = header_checkname(h, FALSE);
if (h->type >= 'a') h->type = htype_other;
- DEBUG(D_receive|D_acl) debug_printf(" %s", header_last->text);
+ DEBUG(D_receive|D_acl) debug_printf_indent(" %s", header_last->text);
}
acl_added_headers = NULL;
-DEBUG(D_receive|D_acl) debug_printf(">>\n");
+DEBUG(D_receive|D_acl) debug_printf_indent(">>\n");
}
header_line *my_headerlist;
uschar *user_msg, *log_msg;
int mime_part_count_buffer = -1;
+uschar * mbox_filename;
int rc = OK;
memset(CS rfc822_file_path,0,2048);
/* check if it is a MIME message */
-my_headerlist = header_list;
-while (my_headerlist != NULL)
- {
- /* skip deleted headers */
- if (my_headerlist->type == '*')
- {
- my_headerlist = my_headerlist->next;
- continue;
- }
- if (strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0)
+
+for (my_headerlist = header_list; my_headerlist; my_headerlist = my_headerlist->next)
+ if ( my_headerlist->type != '*' /* skip deleted headers */
+ && strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0
+ )
{
DEBUG(D_receive) debug_printf("Found Content-Type: header - executing acl_smtp_mime.\n");
goto DO_MIME_ACL;
}
- my_headerlist = my_headerlist->next;
- }
DEBUG(D_receive) debug_printf("No Content-Type: header - presumably not a MIME message.\n");
return TRUE;
DO_MIME_ACL:
+
/* make sure the eml mbox file is spooled up */
-mbox_file = spool_mbox(&mbox_size, NULL);
-if (mbox_file == NULL) {
- /* error while spooling */
+if (!(mbox_file = spool_mbox(&mbox_size, NULL, &mbox_filename)))
+ { /* error while spooling */
log_write(0, LOG_MAIN|LOG_PANIC,
"acl_smtp_mime: error while creating mbox spool file, message temporarily rejected.");
Uunlink(spool_name);
message_id[0] = 0; /* Indicate no message accepted */
*smtp_reply_ptr = US""; /* Indicate reply already sent */
return FALSE; /* Indicate skip to end of receive function */
-};
+ }
mime_is_rfc822 = 0;
/* check if we must check any message/rfc822 attachments */
if (rc == OK)
{
- uschar temp_path[1024];
+ uschar * scandir;
struct dirent * entry;
DIR * tempdir;
- (void) string_format(temp_path, sizeof(temp_path), "%s/scan/%s",
- spool_directory, message_id);
+ scandir = string_copyn(mbox_filename, Ustrrchr(mbox_filename, '/') - mbox_filename);
- tempdir = opendir(CS temp_path);
+ tempdir = opendir(CS scandir);
for (;;)
{
if (!(entry = readdir(tempdir)))
if (strncmpic(US entry->d_name, US"__rfc822_", 9) == 0)
{
(void) string_format(rfc822_file_path, sizeof(rfc822_file_path),
- "%s/scan/%s/%s", spool_directory, message_id, entry->d_name);
- debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n",
+ "%s/%s", scandir, entry->d_name);
+ DEBUG(D_receive) debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n",
rfc822_file_path);
break;
}
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. */
if (extract_recip || !smtp_input)
- cancel_cutthrough_connection("not smtp input");
+ cancel_cutthrough_connection(TRUE, US"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
for (;;)
{
- int ch = (receive_getc)();
+ int ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
/* If we hit EOF on a SMTP connection, it's an error, since incoming
SMTP must have a correct "." terminator. */
if (ptr == 0 && ch == '.' && (smtp_input || dot_ends))
{
- ch = (receive_getc)();
+ ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
if (ch == '\r')
{
- ch = (receive_getc)();
+ ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
if (ch != '\n')
{
receive_ungetc(ch);
if (ch == '\r')
{
- ch = (receive_getc)();
+ ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
if (ch == '\n')
{
if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = TRUE;
if (ch != EOF)
{
- int nextch = (receive_getc)();
+ int nextch = (receive_getc)(GETC_BUFFER_UNLIMITED);
if (nextch == ' ' || nextch == '\t')
{
next->text[ptr++] = nextch;
}
}
+ /* Reject CHUNKING messages that do not CRLF their first header line */
+
+ if (!first_line_ended_crlf && chunking_state > CHUNKING_OFFERED)
+ {
+ log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: "
+ "Non-CRLF-terminated header, under CHUNKING: message abandoned",
+ sender_address,
+ sender_fullhost ? " H=" : "", sender_fullhost ? sender_fullhost : US"",
+ sender_ident ? " U=" : "", sender_ident ? sender_ident : US"");
+ smtp_printf("552 Message header not CRLF terminated\r\n");
+ bdat_flush_data();
+ smtp_reply = US"";
+ goto TIDYUP; /* Skip to end of function */
+ }
+
/* The line has been handled. If we have hit EOF, break out of the loop,
indicating no pending data line. */
DEBUG(D_receive)
{
debug_printf(">>Headers received:\n");
- for (h = header_list->next; h != NULL; h = h->next)
+ for (h = header_list->next; h; h = h->next)
debug_printf("%s", h->text);
debug_printf("\n");
}
/* Scan the headers to identify them. Some are merely marked for later
processing; some are dealt with here. */
-for (h = header_list->next; h != NULL; h = h->next)
+for (h = header_list->next; h; h = h->next)
{
BOOL is_resent = strncmpic(h->text, US"resent-", 7) == 0;
if (is_resent) contains_resent_headers = TRUE;
/* Now scan the headers */
- for (h = header_list->next; h != NULL; h = h->next)
+ for (h = header_list->next; h; h = h->next)
{
if ((h->type == htype_to || h->type == htype_cc || h->type == htype_bcc) &&
(!contains_resent_headers || strncmpic(h->text, US"resent-", 7) == 0))
documented as happening *after* recipient addresses are taken from the headers
by the -t command line option. An added Sender: gets rewritten here. */
-for (h = header_list->next; h != NULL; h = h->next)
+for (h = header_list->next; h; h = h->next)
{
header_line *newh = rewrite_header(h, NULL, NULL, global_rewrite_rules,
rewrite_existflags, TRUE);
- if (newh != NULL) h = newh;
+ if (newh) h = newh;
}
Could we do onward CHUNKING given inbound CHUNKING?
*/
if (chunking_state > CHUNKING_OFFERED)
- cancel_cutthrough_connection("chunking active");
+ cancel_cutthrough_connection(FALSE, US"chunking active");
/* 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 (cutthrough.fd >= 0 && cutthrough.delivery)
{
if (received_count > received_headers_max)
{
- cancel_cutthrough_connection("too many headers");
+ cancel_cutthrough_connection(TRUE, US"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);
+ sender_fullhost ? "H=" : "", sender_fullhost ? sender_fullhost : US"",
+ sender_ident ? "U=" : "", sender_ident ? sender_ident : US"");
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 */
{
if (smtp_input)
{
- message_ended = chunking_state > CHUNKING_OFFERED
- ? read_message_bdat_smtp(data_file)
- : read_message_data_smtp(data_file);
+ message_ended = chunking_state <= CHUNKING_OFFERED
+ ? read_message_data_smtp(data_file)
+ : spool_wireformat
+ ? read_message_bdat_smtp_wire(data_file)
+ : read_message_bdat_smtp(data_file);
receive_linecount++; /* The terminating "." line */
}
else message_ended = read_message_data(data_file);
if (smtp_input)
{
Uunlink(spool_name); /* Lose data file when closed */
- cancel_cutthrough_connection("sender closed connection");
+ cancel_cutthrough_connection(TRUE, US"sender closed connection");
message_id[0] = 0; /* Indicate no message accepted */
smtp_reply = handle_lost_connection(US"");
smtp_yield = FALSE;
case END_SIZE:
Uunlink(spool_name); /* Lose the data file when closed */
- cancel_cutthrough_connection("mail too big");
+ cancel_cutthrough_connection(TRUE, US"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: "
case END_PROTOCOL:
Uunlink(spool_name); /* Lose the data file when closed */
- cancel_cutthrough_connection("sender protocol error");
+ cancel_cutthrough_connection(TRUE, US"sender protocol error");
smtp_reply = US""; /* Response already sent */
message_id[0] = 0; /* Indicate no message accepted */
goto TIDYUP; /* Skip to end of function */
log_write(0, LOG_MAIN, "Message abandoned: %s", msg);
Uunlink(spool_name); /* Lose the data file */
- cancel_cutthrough_connection("error writing spoolfile");
+ cancel_cutthrough_connection(TRUE, US"error writing spoolfile");
if (smtp_input)
{
DEBUG(D_receive)
debug_printf("acl_smtp_dkim: acl_check returned %d on %s, "
"skipping remaining items\n", rc, item);
- cancel_cutthrough_connection("dkim acl not ok");
+ cancel_cutthrough_connection(TRUE, US"dkim acl not ok");
break;
}
}
{
recipients_count = 0;
blackholed_by = US"DATA ACL";
- if (log_msg != NULL)
+ if (log_msg)
blackhole_log_msg = string_sprintf(": %s", log_msg);
- cancel_cutthrough_connection("data acl discard");
+ cancel_cutthrough_connection(TRUE, US"data acl discard");
}
else if (rc != OK)
{
Uunlink(spool_name);
- cancel_cutthrough_connection("data acl not ok");
+ cancel_cutthrough_connection(TRUE, US"data acl not ok");
#ifdef WITH_CONTENT_SCAN
unspool_mbox();
#endif
}
#endif
-/* Update the timstamp in our Received: header to account for any time taken by
+/* Update the timestamp in our Received: header to account for any time taken by
an ACL or by local_scan(). The new time is the time that all reception
processing is complete. */
macro to simplify the coding. We log the arrival of a new message while the
file is still locked, just in case the machine is *really* fast, and delivers
it first! Include any message id that is in the message - since the syntax of a
-message id is actually an addr-spec, we can use the parse routine to canonicize
+message id is actually an addr-spec, we can use the parse routine to canonicalize
it. */
size = 256;
if (select(fileno(smtp_in) + 1, &select_check, NULL, NULL, &tv) != 0)
{
- int c = (receive_getc)();
+ int c = (receive_getc)(GETC_BUFFER_UNLIMITED);
if (c != EOF) (receive_ungetc)(c); else
{
- uschar *msg = US"SMTP connection lost after final dot";
+ smtp_notquit_exit(US"connection-lost", NULL, NULL);
smtp_reply = US""; /* No attempt to send a response */
smtp_yield = FALSE; /* Nothing more on this connection */
/* Re-use the log line workspace */
sptr = 0;
- s = string_cat(s, &size, &sptr, msg);
+ s = string_cat(s, &size, &sptr, US"SMTP connection lost after final dot");
s = add_host_info_for_log(s, &size, &sptr);
s[sptr] = 0;
log_write(0, LOG_MAIN, "%s", s);
Send dot onward. If accepted, wipe the spooled files, log as delivered and accept
the sender's dot (below).
- If rejected: copy response to sender, wipe the spooled files, log approriately.
- If temp-reject: accept to sender, keep the spooled files.
+ If rejected: copy response to sender, wipe the spooled files, log appropriately.
+ If temp-reject: normally accept to sender, keep the spooled file - unless defer=pass
+ in which case pass temp-reject back to initiator and dump the 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.
*/
-if(cutthrough.fd >= 0)
+if(cutthrough.fd >= 0 && cutthrough.delivery)
{
- uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the message */
+ uschar * msg = cutthrough_finaldot(); /* Ask the target system to accept the message */
/* Logging was done in finaldot() */
switch(msg[0])
{
cutthrough_done = ACCEPTED;
break; /* message_id needed for SMTP accept below */
+ case '4': /* Temp-reject. Keep spoolfiles and accept, unless defer-pass mode.
+ ... for which, pass back the exact error */
+ if (cutthrough.defer_pass) smtp_reply = string_copy_malloc(msg);
+ /*FALLTRHOUGH*/
+
default: /* Unknown response, or error. Treat as temp-reject. */
- case '4': /* Temp-reject. Keep spoolfiles and accept. */
cutthrough_done = TMP_REJ; /* 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 */
+ smtp_reply = string_copy_malloc(msg); /* Pass on the exact error */
cutthrough_done = PERM_REJ;
break;
}
else if (chunking_state > CHUNKING_OFFERED)
{
+/*XXX rethink for spool_wireformat */
smtp_printf("250- %u byte chunk, total %d\r\n250 OK id=%s\r\n",
chunking_datasize, message_size+message_linecount, message_id);
chunking_state = CHUNKING_OFFERED;
/* smtp_reply is set non-empty */
else if (smtp_reply[0] != 0)
- {
if (fake_response != OK && (smtp_reply[0] == '2'))
smtp_respond((fake_response == DEFER)? US"450" : US"550", 3, TRUE,
fake_response_text);
else
smtp_printf("%.1024s\r\n", smtp_reply);
- }
switch (cutthrough_done)
{
- case ACCEPTED: log_write(0, LOG_MAIN, "Completed");/* Delivery was done */
+ case ACCEPTED:
+ log_write(0, LOG_MAIN, "Completed");/* Delivery was done */
case PERM_REJ:
- { /* Delete spool files */
- Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D"));
- Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
- Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
- }
- case TMP_REJ: message_id[0] = 0; /* Prevent a delivery from starting */
- default:break;
+ /* Delete spool files */
+ Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D"));
+ Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
+ Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
+ break;
+
+ case TMP_REJ:
+ if (cutthrough.defer_pass)
+ {
+ Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D"));
+ Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
+ Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
+ }
+ default:
+ break;
+ }
+ if (cutthrough_done != NOT_TRIED)
+ {
+ message_id[0] = 0; /* Prevent a delivery from starting */
+ cutthrough.delivery = cutthrough.callout_hold_only = FALSE;
+ cutthrough.defer_pass = FALSE;
}
- cutthrough.delivery = FALSE;
}
/* For batched SMTP, generate an error message on failure, and do