X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/d4e5e70b6c47bc30f9d6ce8300326ffc9fde79f1..544dd905b208164eaae771ab6d2a198b4c67aa0c:/src/src/receive.c diff --git a/src/src/receive.c b/src/src/receive.c index 67fcc8e15..ed2c57ba5 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -25,6 +25,7 @@ static FILE *data_file = NULL; static int data_fd = -1; static uschar *spool_name = US""; +enum CH_STATE {LF_SEEN, MID_LINE, CR_SEEN}; /************************************************* @@ -905,7 +906,8 @@ 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 */ @@ -913,27 +915,55 @@ Returns: One of the END_xxx values indicating why it stopped reading static int read_message_bdat_smtp(FILE *fout) { -int ch_state = 0, linelength = 0, ch; +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 EOD: return END_DOT; /* normal exit */ 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. + */ + fseek(fout, -1, SEEK_CUR); + 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 0: /* After LF or CRLF */ - ch_state = 1; + case LF_SEEN: /* After LF or CRLF */ + ch_state = MID_LINE; /* fall through to handle as normal uschar. */ - case 1: /* Mid-line state */ + case MID_LINE: /* Mid-line state */ if (ch == '\n') { - ch_state = 0; + ch_state = LF_SEEN; body_linecount++; if (linelength > max_received_linelength) max_received_linelength = linelength; @@ -941,25 +971,26 @@ for(;;) } else if (ch == '\r') { - ch_state = 2; + ch_state = CR_SEEN; + if (fix_nl) bdat_ungetc('\n'); continue; /* don't write CR */ } break; - case 2: /* After (unwritten) CR */ + case CR_SEEN: /* After (unwritten) CR */ body_linecount++; if (linelength > max_received_linelength) max_received_linelength = linelength; linelength = -1; if (ch == '\n') - ch_state = 0; + ch_state = LF_SEEN; else { message_size++; if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; (void) cutthrough_put_nl(); if (ch == '\r') continue; /* don't write CR */ - ch_state = 1; + ch_state = MID_LINE; } break; } @@ -1116,7 +1147,7 @@ switch(where) if (acl_removed_headers != NULL) { - 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) { @@ -1129,15 +1160,15 @@ if (acl_removed_headers != NULL) 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); +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) { @@ -1148,7 +1179,7 @@ for (h = acl_added_headers; h != NULL; 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: @@ -1163,7 +1194,7 @@ for (h = acl_added_headers; h != NULL; h = next) } 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: @@ -1178,7 +1209,7 @@ for (h = acl_added_headers; h != NULL; h = next) 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: @@ -1198,11 +1229,11 @@ for (h = acl_added_headers; h != NULL; h = next) 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"); } @@ -1354,7 +1385,7 @@ if (rc == OK) { (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", + DEBUG(D_receive) debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path); break; }