X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4a8ce2d88e1b24b456199ec5f2a799a6cb22ae91..7e3ce68e68ab9b8906a637d352993abf361554e2:/src/src/receive.c diff --git a/src/src/receive.c b/src/src/receive.c index 1b4656cfc..9ff339d39 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for receiving a message and setting up spool files. */ @@ -14,7 +14,7 @@ extern int dcc_ok; #endif #ifdef EXPERIMENTAL_DMARC -#include "dmarc.h" +# include "dmarc.h" #endif /* EXPERIMENTAL_DMARC */ /************************************************* @@ -23,7 +23,7 @@ extern int dcc_ok; static FILE *data_file = NULL; static int data_fd = -1; -static uschar spool_name[256]; +static uschar *spool_name = US""; @@ -87,7 +87,7 @@ 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, &untrusted_set_sender, NULL, -1, + match_address_list(qnewsender, TRUE, TRUE, CUSS &untrusted_set_sender, NULL, -1, 0, NULL) == OK; } @@ -142,17 +142,16 @@ appearance of "syslog" in it. */ else { int sep = ':'; /* Not variable - outside scripts use */ - uschar *p = log_file_path; + const uschar *p = log_file_path; name = US"log"; /* An empty log_file_path means "use the default". This is the same as an empty item in a list. */ if (*p == 0) p = US":"; - while ((path = string_nextinlist(&p, &sep, buffer, sizeof(buffer))) != NULL) - { - if (Ustrcmp(path, "syslog") != 0) break; - } + while ((path = string_nextinlist(&p, &sep, buffer, sizeof(buffer)))) + if (Ustrcmp(path, "syslog") != 0) + break; if (path == NULL) /* No log files */ { @@ -497,6 +496,8 @@ recipients_list[recipients_count].bmi_optin = bmi_current_optin; /* reset optin string pointer for next recipient */ bmi_current_optin = NULL; #endif +recipients_list[recipients_count].orcpt = NULL; +recipients_list[recipients_count].dsn_flags = 0; recipients_list[recipients_count++].errors_to = NULL; } @@ -519,12 +520,12 @@ Arguments: Returns: nothing */ -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR static void smtp_user_msg(uschar *code, uschar *user_msg) { int len = 3; -smtp_message_code(&code, &len, &user_msg, NULL); +smtp_message_code(&code, &len, &user_msg, NULL, TRUE); smtp_respond(code, len, TRUE, user_msg); } #endif @@ -680,6 +681,7 @@ while ((ch = (receive_getc)()) != EOF) case 1: /* After written "\n" */ if (ch == '.') { ch_state = 3; continue; } + if (ch == '\r') { ch_state = 2; continue; } if (ch != '\n') ch_state = 0; else linelength = -1; break; @@ -774,7 +776,7 @@ read_message_data_smtp(FILE *fout) { int ch_state = 0; int ch; -register int linelength = 0; +int linelength = 0; while ((ch = (receive_getc)()) != EOF) { @@ -833,7 +835,15 @@ while ((ch = (receive_getc)()) != EOF) ch_state = 4; continue; } - ch_state = 1; /* The dot itself is removed */ + /* The dot was removed at state 3. For a doubled dot, here, reinstate + it to cutthrough. The current ch, dot or not, is passed both to cutthrough + and to file below. */ + if (ch == '.') + { + uschar c= ch; + (void) cutthrough_puts(&c, 1); + } + ch_state = 1; break; case 4: /* After [CR] LF . CR */ @@ -856,7 +866,7 @@ while ((ch = (receive_getc)()) != EOF) message_size++; linelength++; - if (fout != NULL) + if (fout) { if (fputc(ch, fout) == EOF) return END_WERROR; if (message_size > thismessage_size_limit) return END_SIZE; @@ -865,7 +875,7 @@ while ((ch = (receive_getc)()) != EOF) (void) cutthrough_put_nl(); else { - uschar c= ch; + uschar c = ch; (void) cutthrough_puts(&c, 1); } } @@ -879,6 +889,63 @@ return END_EOF; +/* 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. + +Arguments: + fout a FILE to which to write the message; NULL if skipping + +Returns: One of the END_xxx values indicating why it stopped reading +*/ + +static int +read_message_bdat_smtp(FILE *fout) +{ +int ch; +int linelength = 0; + +for (;;) switch (ch = bdat_getc()) + { + 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++; + 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*/ +} + + + + /************************************************* * Swallow SMTP message * *************************************************/ @@ -895,6 +962,7 @@ Returns: nothing void receive_swallow_smtp(void) { +/*XXX CHUNKING: not enough. read chunks until RSET? */ if (message_ended >= END_NOTENDED) message_ended = read_message_data_smtp(NULL); } @@ -951,10 +1019,12 @@ if (error_handling == ERRORS_SENDER) error_block eblock; eblock.next = NULL; eblock.text1 = text1; + eblock.text2 = US""; if (!moan_to_sender(errcode, &eblock, hptr, f, FALSE)) error_rc = EXIT_FAILURE; } -else fprintf(stderr, "exim: %s%s\n", text2, text1); /* Sic */ +else + fprintf(stderr, "exim: %s%s\n", text2, text1); /* Sic */ (void)fclose(f); exim_exit(error_rc); } @@ -984,38 +1054,41 @@ Returns: nothing */ static void -add_acl_headers(uschar *acl_name) +add_acl_headers(int where, uschar *acl_name) { header_line *h, *next; header_line *last_received = NULL; +switch(where) + { + case ACL_WHERE_DKIM: + case ACL_WHERE_MIME: + case ACL_WHERE_DATA: + if (cutthrough.fd >= 0 && (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"); + return; + } + } + 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) + for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) { - uschar *list; - BOOL include_header; - - if (h->type == htype_old) continue; - - include_header = TRUE; - list = acl_removed_headers; - + const uschar * 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)) + + while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) + if (header_testname(h, s, Ustrlen(s), 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"); @@ -1108,17 +1181,17 @@ Returns: the extended string */ static uschar * -add_host_info_for_log(uschar *s, int *sizeptr, int *ptrptr) +add_host_info_for_log(uschar * s, int * sizeptr, int * ptrptr) { -if (sender_fullhost != NULL) +if (sender_fullhost) { + if (LOGGING(dnssec) && sender_host_dnssec) /*XXX sender_helo_dnssec? */ + s = string_cat(s, sizeptr, ptrptr, US" DS"); s = string_append(s, sizeptr, ptrptr, 2, US" H=", sender_fullhost); - if ((log_extra_selector & LX_incoming_interface) != 0 && - interface_address != NULL) + if (LOGGING(incoming_interface) && interface_address != NULL) { - uschar *ss = string_sprintf(" I=[%s]:%d", interface_address, - interface_port); - s = string_cat(s, sizeptr, ptrptr, ss, Ustrlen(ss)); + s = string_cat(s, sizeptr, ptrptr, + string_sprintf(" I=[%s]:%d", interface_address, interface_port)); } } if (sender_ident != NULL) @@ -1224,47 +1297,45 @@ if (Ustrlen(rfc822_file_path) > 0) if (rc == OK) { uschar temp_path[1024]; - int n; - struct dirent *entry; - DIR *tempdir; + struct dirent * entry; + DIR * tempdir; - (void)string_format(temp_path, 1024, "%s/scan/%s", spool_directory, - message_id); + (void) string_format(temp_path, sizeof(temp_path), "%s/scan/%s", + spool_directory, message_id); tempdir = opendir(CS temp_path); - n = 0; - do + for (;;) { - entry = readdir(tempdir); - if (entry == NULL) break; - if (strncmpic(US entry->d_name,US"__rfc822_",9) == 0) + if (!(entry = readdir(tempdir))) + break; + if (strncmpic(US entry->d_name, US"__rfc822_", 9) == 0) { - (void)string_format(rfc822_file_path, 2048,"%s/scan/%s/%s", spool_directory, message_id, entry->d_name); - debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path); + (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", + rfc822_file_path); break; } - } while (1); + } closedir(tempdir); - if (entry != NULL) + if (entry) { - mbox_file = Ufopen(rfc822_file_path,"rb"); - if (mbox_file == NULL) + if ((mbox_file = Ufopen(rfc822_file_path, "rb"))) { - log_write(0, LOG_PANIC, - "acl_smtp_mime: can't open RFC822 spool file, skipping."); - unlink(CS rfc822_file_path); - goto END_MIME_ACL; + /* set RFC822 expansion variable */ + mime_is_rfc822 = 1; + mime_part_count_buffer = mime_part_count; + goto MIME_ACL_CHECK; } - /* set RFC822 expansion variable */ - mime_is_rfc822 = 1; - mime_part_count_buffer = mime_part_count; - goto MIME_ACL_CHECK; + log_write(0, LOG_PANIC, + "acl_smtp_mime: can't open RFC822 spool file, skipping."); + unlink(CS rfc822_file_path); } } END_MIME_ACL: -add_acl_headers(US"MIME"); +add_acl_headers(ACL_WHERE_MIME, US"MIME"); if (rc == DISCARD) { recipients_count = 0; @@ -1277,9 +1348,12 @@ else if (rc != OK) #ifdef EXPERIMENTAL_DCC dcc_ok = 0; #endif - if (smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0) - *smtp_yield_ptr = FALSE; /* No more messsages after dropped connection */ - *smtp_reply_ptr = US""; /* Indicate reply already sent */ + if ( smtp_input + && smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0) + { + *smtp_yield_ptr = FALSE; /* No more messages after dropped connection */ + *smtp_reply_ptr = US""; /* Indicate reply already sent */ + } message_id[0] = 0; /* Indicate no message accepted */ return FALSE; /* Cause skip to end of receive function */ } @@ -1303,7 +1377,7 @@ if (recipients_count == 1) received_for = recipients_list[0].address; received = expand_string(received_header_text); received_for = NULL; -if (received == NULL) +if (!received) { if(spool_name[0] != 0) Uunlink(spool_name); /* Lose the data file */ @@ -1453,7 +1527,7 @@ BOOL resents_exist = FALSE; uschar *resent_prefix = US""; uschar *blackholed_by = NULL; uschar *blackhole_log_msg = US""; -int cutthrough_done = 0; +enum {NOT_TRIED, TMP_REJ, PERM_REJ, ACCEPTED} cutthrough_done = NOT_TRIED; flock_t lock_data; error_block *bad_addresses = NULL; @@ -1527,7 +1601,7 @@ yet, initialize the size and warning count, and deal with no size limit. */ message_id[0] = 0; data_file = NULL; data_fd = -1; -spool_name[0] = 0; +spool_name = US""; message_size = 0; warning_count = 0; received_count = 1; /* For the one we will add */ @@ -2299,9 +2373,23 @@ if (extract_recip) pp = recipient = store_get(ss - s + 1); for (p = s; p < ss; p++) if (*p != '\n') *pp++ = *p; *pp = 0; + +#ifdef SUPPORT_I18N + { + BOOL b = allow_utf8_domains; + allow_utf8_domains = TRUE; +#endif recipient = parse_extract_address(recipient, &errmess, &start, &end, &domain, FALSE); +#ifdef SUPPORT_I18N + if (string_is_utf8(recipient)) + message_smtputf8 = TRUE; + else + allow_utf8_domains = b; + } +#endif + /* Keep a list of all the bad addresses so we can send a single error message at the end. However, an empty address is not an error; just ignore it. This can come from an empty group list like @@ -2442,7 +2530,7 @@ it will fit. */ to be the least significant base-62 digit of the time of arrival. Otherwise ensure that it is an empty string. */ -message_subdir[0] = split_spool_directory? message_id[5] : 0; +message_subdir[0] = split_spool_directory ? message_id[5] : 0; /* Now that we have the message-id, if there is no message-id: header, generate one, but only for local (without suppress_local_fixups) or submission mode @@ -2511,7 +2599,7 @@ if (msgid_header == NULL && rewriting. Must copy the count, because later ACLs and the local_scan() function may mess with the real recipients. */ -if ((log_extra_selector & LX_received_recipients) != 0) +if (LOGGING(received_recipients)) { raw_recipients = store_get(recipients_count * sizeof(uschar *)); for (i = 0; i < recipients_count; i++) @@ -2801,13 +2889,20 @@ if (filter_test != FTEST_NONE) return message_ended == END_DOT; } -/* 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. +/*XXX CHUNKING: need to cancel cutthrough under BDAT, for now. In future, +think more if it could be handled. Cannot do onward CHUNKING unless +inbound is, but inbound chunking ought to be ok with outbound plain. +Could we do onward CHUNKING given inbound CHUNKING? */ -if (cutthrough_fd >= 0) +if (chunking_state > CHUNKING_OFFERED) + cancel_cutthrough_connection("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 (received_count > received_headers_max) { @@ -2825,27 +2920,25 @@ if (cutthrough_fd >= 0) goto TIDYUP; /* Skip to end of function */ } received_header_gen(); - add_acl_headers(US"MAIL or RCPT"); + add_acl_headers(ACL_WHERE_RCPT, US"MAIL or RCPT"); (void) cutthrough_headers_send(); } /* 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 -directory if it isn't there. Note re use of sprintf: spool_directory -is checked on input to be < 200 characters long. */ +directory if it isn't there. */ -sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory, message_subdir, - message_id); -data_fd = Uopen(spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE); -if (data_fd < 0) +spool_name = spool_fname(US"input", message_subdir, message_id, US"-D"); +DEBUG(D_receive) debug_printf("Data file name: %s\n", spool_name); + +if ((data_fd = Uopen(spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE)) < 0) { if (errno == ENOENT) { - uschar temp[16]; - sprintf(CS temp, "input/%s", message_subdir); - if (message_subdir[0] == 0) temp[5] = 0; - (void)directory_make(spool_directory, temp, INPUT_DIRECTORY_MODE, TRUE); + (void) directory_make(spool_directory, + spool_sname(US"input", message_subdir), + INPUT_DIRECTORY_MODE, TRUE); data_fd = Uopen(spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE); } if (data_fd < 0) @@ -2901,7 +2994,9 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) { if (smtp_input) { - message_ended = read_message_data_smtp(data_file); + message_ended = chunking_state > CHUNKING_OFFERED + ? read_message_bdat_smtp(data_file) + : read_message_data_smtp(data_file); receive_linecount++; /* The terminating "." line */ } else message_ended = read_message_data(data_file); @@ -2909,51 +3004,64 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) receive_linecount += body_linecount; /* For BSMTP errors mainly */ message_linecount += body_linecount; - /* Handle premature termination of SMTP */ - - if (smtp_input && message_ended == END_EOF) + switch (message_ended) { - Uunlink(spool_name); /* Lose data file when closed */ - cancel_cutthrough_connection("sender closed connection"); - message_id[0] = 0; /* Indicate no message accepted */ - smtp_reply = handle_lost_connection(US""); - smtp_yield = FALSE; - goto TIDYUP; /* Skip to end of function */ - } + /* Handle premature termination of SMTP */ - /* Handle message that is too big. Don't use host_or_ident() in the log - message; we want to see the ident value even for non-remote messages. */ + case END_EOF: + if (smtp_input) + { + Uunlink(spool_name); /* Lose data file when closed */ + cancel_cutthrough_connection("sender closed connection"); + message_id[0] = 0; /* Indicate no message accepted */ + smtp_reply = handle_lost_connection(US""); + smtp_yield = FALSE; + goto TIDYUP; /* Skip to end of function */ + } + break; - if (message_ended == END_SIZE) - { - Uunlink(spool_name); /* Lose the data file when closed */ - cancel_cutthrough_connection("mail too big"); - if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */ + /* Handle message that is too big. Don't use host_or_ident() in the log + message; we want to see the ident value even for non-remote messages. */ - log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " - "message too big: read=%d max=%d", - sender_address, - (sender_fullhost == NULL)? "" : " H=", - (sender_fullhost == NULL)? US"" : sender_fullhost, - (sender_ident == NULL)? "" : " U=", - (sender_ident == NULL)? US"" : sender_ident, - message_size, - thismessage_size_limit); + case END_SIZE: + Uunlink(spool_name); /* Lose the data file when closed */ + cancel_cutthrough_connection("mail too big"); + if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */ - if (smtp_input) - { - smtp_reply = US"552 Message size exceeds maximum permitted"; - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ - } - else - { - fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); - give_local_error(ERRMESS_TOOBIG, - string_sprintf("message too big (max=%d)", thismessage_size_limit), - US"message rejected: ", error_rc, data_file, header_list); - /* Does not return */ - } + log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " + "message too big: read=%d max=%d", + sender_address, + (sender_fullhost == NULL)? "" : " H=", + (sender_fullhost == NULL)? US"" : sender_fullhost, + (sender_ident == NULL)? "" : " U=", + (sender_ident == NULL)? US"" : sender_ident, + message_size, + thismessage_size_limit); + + if (smtp_input) + { + smtp_reply = US"552 Message size exceeds maximum permitted"; + message_id[0] = 0; /* Indicate no message accepted */ + goto TIDYUP; /* Skip to end of function */ + } + else + { + fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + give_local_error(ERRMESS_TOOBIG, + string_sprintf("message too big (max=%d)", thismessage_size_limit), + US"message rejected: ", error_rc, data_file, header_list); + /* Does not return */ + } + break; + + /* Handle bad BDAT protocol sequence */ + + case END_PROTOCOL: + Uunlink(spool_name); /* Lose the data file when closed */ + cancel_cutthrough_connection("sender protocol error"); + smtp_reply = US""; /* Response already sent */ + message_id[0] = 0; /* Indicate no message accepted */ + goto TIDYUP; /* Skip to end of function */ } } @@ -3117,7 +3225,7 @@ if (received_header->text == NULL) /* Non-cutthrough case */ /* If an ACL from any RCPT commands set up any warning headers to add, do so now, before running the DATA ACL. */ - add_acl_headers(US"MAIL or RCPT"); + add_acl_headers(ACL_WHERE_RCPT, US"MAIL or RCPT"); } else message_body_size = (fstat(data_fd, &statbuf) == 0)? @@ -3136,9 +3244,8 @@ user_msg = NULL; enable_dollar_recipients = TRUE; if (recipients_count == 0) - { - blackholed_by = recipients_discarded? US"MAIL ACL" : US"RCPT ACL"; - } + blackholed_by = recipients_discarded ? US"MAIL ACL" : US"RCPT ACL"; + else { /* Handle interactive SMTP messages */ @@ -3154,83 +3261,80 @@ else dkim_exim_verify_finish(); /* Check if we must run the DKIM ACL */ - if ((acl_smtp_dkim != NULL) && - (dkim_verify_signers != NULL) && - (dkim_verify_signers[0] != '\0')) + if (acl_smtp_dkim && dkim_verify_signers && *dkim_verify_signers) { uschar *dkim_verify_signers_expanded = expand_string(dkim_verify_signers); - if (dkim_verify_signers_expanded == NULL) - { + if (!dkim_verify_signers_expanded) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of dkim_verify_signers option failed: %s", expand_string_message); - } + else { int sep = 0; - uschar *ptr = dkim_verify_signers_expanded; + const uschar *ptr = dkim_verify_signers_expanded; uschar *item = NULL; uschar *seen_items = NULL; int seen_items_size = 0; int seen_items_offset = 0; - uschar itembuf[256]; /* Default to OK when no items are present */ rc = OK; - while ((item = string_nextinlist(&ptr, &sep, - itembuf, - sizeof(itembuf))) != NULL) + while ((item = string_nextinlist(&ptr, &sep, NULL, 0))) { /* Prevent running ACL for an empty item */ - if (!item || (item[0] == '\0')) continue; - /* Only run ACL once for each domain or identity, no matter how often it - appears in the expanded list. */ - if (seen_items != NULL) + if (!item || !*item) continue; + + /* Only run ACL once for each domain or identity, + no matter how often it appears in the expanded list. */ + if (seen_items) { uschar *seen_item = NULL; - uschar seen_item_buf[256]; - uschar *seen_items_list = seen_items; - int seen_this_item = 0; + const uschar *seen_items_list = seen_items; + BOOL seen_this_item = FALSE; while ((seen_item = string_nextinlist(&seen_items_list, &sep, - seen_item_buf, - sizeof(seen_item_buf))) != NULL) - { - if (Ustrcmp(seen_item,item) == 0) - { - seen_this_item = 1; - break; - } - } - - if (seen_this_item > 0) + NULL, 0))) + if (Ustrcmp(seen_item,item) == 0) + { + seen_this_item = TRUE; + break; + } + + if (seen_this_item) { DEBUG(D_receive) - debug_printf("acl_smtp_dkim: skipping signer %s, already seen\n", item); + debug_printf("acl_smtp_dkim: skipping signer %s, " + "already seen\n", item); continue; } - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":"); + seen_items = string_append(seen_items, &seen_items_size, + &seen_items_offset, 1, ":"); } - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,item); + seen_items = string_append(seen_items, &seen_items_size, + &seen_items_offset, 1, item); seen_items[seen_items_offset] = '\0'; DEBUG(D_receive) - debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", item); + debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", + item); dkim_exim_acl_setup(item); - rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, &user_msg, &log_msg); + rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, + &user_msg, &log_msg); if (rc != OK) - { - 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"); - break; - } + { + 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"); + break; + } } - add_acl_headers(US"DKIM"); + add_acl_headers(ACL_WHERE_DKIM, US"DKIM"); if (rc == DISCARD) { recipients_count = 0; @@ -3242,7 +3346,7 @@ else { Uunlink(spool_name); if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0) - smtp_yield = FALSE; /* No more messsages after dropped connection */ + smtp_yield = FALSE; /* No more messages after dropped connection */ smtp_reply = US""; /* Indicate reply already sent */ message_id[0] = 0; /* Indicate no message accepted */ goto TIDYUP; /* Skip to end of function */ @@ -3263,8 +3367,8 @@ else dmarc_up = dmarc_store_data(from_header); #endif /* EXPERIMENTAL_DMARC */ -#ifdef EXPERIMENTAL_PRDR - if (prdr_requested && recipients_count > 1 && acl_smtp_data_prdr != NULL ) +#ifndef DISABLE_PRDR + if (prdr_requested && recipients_count > 1 && acl_smtp_data_prdr) { unsigned int c; int all_pass = OK; @@ -3311,7 +3415,7 @@ else } if (log_msg) log_write(0, LOG_MAIN, "PRDR %s %s", addr, log_msg); else if (user_msg) log_write(0, LOG_MAIN, "PRDR %s %s", addr, user_msg); - else log_write(0, LOG_MAIN, CS msg); + else log_write(0, LOG_MAIN, "%s", CS msg); if (rc != OK) { receive_remove_recipient(addr); c--; } } @@ -3332,7 +3436,7 @@ else } else prdr_requested = FALSE; -#endif /* EXPERIMENTAL_PRDR */ +#endif /* !DISABLE_PRDR */ /* Check the recipients count again, as the MIME ACL might have changed them. */ @@ -3340,7 +3444,7 @@ else if (acl_smtp_data != NULL && recipients_count > 0) { rc = acl_check(ACL_WHERE_DATA, NULL, acl_smtp_data, &user_msg, &log_msg); - add_acl_headers(US"DATA"); + add_acl_headers(ACL_WHERE_DATA, US"DATA"); if (rc == DISCARD) { recipients_count = 0; @@ -3360,7 +3464,7 @@ else dcc_ok = 0; #endif if (smtp_handle_acl_fail(ACL_WHERE_DATA, rc, user_msg, log_msg) != 0) - smtp_yield = FALSE; /* No more messsages after dropped connection */ + smtp_yield = FALSE; /* No more messages after dropped connection */ smtp_reply = US""; /* Indicate reply already sent */ message_id[0] = 0; /* Indicate no message accepted */ goto TIDYUP; /* Skip to end of function */ @@ -3423,7 +3527,7 @@ else /* Does not return */ } } - add_acl_headers(US"non-SMTP"); + add_acl_headers(ACL_WHERE_NOTSMTP, US"non-SMTP"); } } @@ -3551,7 +3655,7 @@ else goto TEMPREJECT; case LOCAL_SCAN_REJECT_NOLOGHDR: - log_extra_selector &= ~LX_rejected_header; + BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header); /* Fall through */ case LOCAL_SCAN_REJECT: @@ -3560,7 +3664,7 @@ else break; case LOCAL_SCAN_TEMPREJECT_NOLOGHDR: - log_extra_selector &= ~LX_rejected_header; + BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header); /* Fall through */ case LOCAL_SCAN_TEMPREJECT: @@ -3616,11 +3720,11 @@ signal(SIGINT, SIG_IGN); deliver_firsttime = TRUE; #ifdef EXPERIMENTAL_BRIGHTMAIL -if (bmi_run == 1) { - /* rewind data file */ +if (bmi_run == 1) + { /* rewind data file */ lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); bmi_verdicts = bmi_process_message(header_list, data_fd); -}; + } #endif /* Update the timstamp in our Received: header to account for any time taken by @@ -3658,7 +3762,6 @@ if (host_checking || blackholed_by != NULL) /* Write the -H file */ else - { if ((msg_size = spool_write_header(message_id, SW_RECEIVING, &errmsg)) < 0) { log_write(0, LOG_MAIN, "Message abandoned: %s", errmsg); @@ -3678,7 +3781,6 @@ else /* Does not return */ } } - } /* The message has now been successfully received. */ @@ -3725,36 +3827,43 @@ if (message_reference != NULL) s = add_host_info_for_log(s, &size, &sptr); #ifdef SUPPORT_TLS -if ((log_extra_selector & LX_tls_cipher) != 0 && tls_in.cipher != NULL) +if (LOGGING(tls_cipher) && tls_in.cipher) s = string_append(s, &size, &sptr, 2, US" X=", tls_in.cipher); -if ((log_extra_selector & LX_tls_certificate_verified) != 0 && - tls_in.cipher != NULL) +if (LOGGING(tls_certificate_verified) && tls_in.cipher) s = string_append(s, &size, &sptr, 2, US" CV=", tls_in.certificate_verified? "yes":"no"); -if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_in.peerdn != NULL) +if (LOGGING(tls_peerdn) && tls_in.peerdn) s = string_append(s, &size, &sptr, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\""); -if ((log_extra_selector & LX_tls_sni) != 0 && tls_in.sni != NULL) +if (LOGGING(tls_sni) && tls_in.sni) s = string_append(s, &size, &sptr, 3, US" SNI=\"", string_printing(tls_in.sni), US"\""); #endif -if (sender_host_authenticated != NULL) +if (sender_host_authenticated) { 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) + if (LOGGING(smtp_mailauth) && authenticated_sender != NULL) s = string_append(s, &size, &sptr, 2, US":", authenticated_sender); } } -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR if (prdr_requested) - s = string_append(s, &size, &sptr, 1, US" PRDR"); + s = string_catn(s, &size, &sptr, US" PRDR", 5); +#endif + +#ifdef SUPPORT_PROXY +if (proxy_session && LOGGING(proxy)) + s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_local_address); #endif +if (chunking_state > CHUNKING_OFFERED) + s = string_catn(s, &size, &sptr, US" K", 2); + sprintf(CS big_buffer, "%d", msg_size); s = string_append(s, &size, &sptr, 2, US" S=", big_buffer); @@ -3762,12 +3871,15 @@ s = string_append(s, &size, &sptr, 2, US" S=", big_buffer); 0 ... no BODY= used 7 ... 7BIT 8 ... 8BITMIME */ -if (log_extra_selector & LX_8bitmime) +if (LOGGING(8bitmime)) { sprintf(CS big_buffer, "%d", body_8bitmime); s = string_append(s, &size, &sptr, 2, US" M8S=", big_buffer); } +if (*queue_name) + s = string_append(s, &size, &sptr, 2, US" Q=", queue_name); + /* 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. @@ -3788,7 +3900,7 @@ if (msgid_header != NULL) /* If subject logging is turned on, create suitable printing-character text. By expanding $h_subject: we make use of the MIME decoding. */ -if ((log_extra_selector & LX_subject) != 0 && subject_header != NULL) +if (LOGGING(subject) && subject_header != NULL) { int i; uschar *p = big_buffer; @@ -3822,16 +3934,15 @@ if (message_logs && blackholed_by == NULL) { int fd; - sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory, message_subdir, - message_id); - fd = Uopen(spool_name, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE); - - if (fd < 0 && errno == ENOENT) + spool_name = spool_fname(US"msglog", message_subdir, message_id, US""); + + if ( (fd = Uopen(spool_name, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE)) < 0 + && errno == ENOENT + ) { - uschar temp[16]; - sprintf(CS temp, "msglog/%s", message_subdir); - if (message_subdir[0] == 0) temp[6] = 0; - (void)directory_make(spool_directory, temp, MSGLOG_DIRECTORY_MODE, TRUE); + (void)directory_make(spool_directory, + spool_sname(US"msglog", message_subdir), + MSGLOG_DIRECTORY_MODE, TRUE); fd = Uopen(spool_name, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE); } @@ -3909,24 +4020,16 @@ if (smtp_input && sender_host_address != NULL && !sender_host_notsocket && /* Re-use the log line workspace */ sptr = 0; - s = string_cat(s, &size, &sptr, msg, Ustrlen(msg)); + s = string_cat(s, &size, &sptr, msg); s = add_host_info_for_log(s, &size, &sptr); s[sptr] = 0; log_write(0, LOG_MAIN, "%s", s); /* Delete the files for this aborted message. */ - 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); + 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"")); goto TIDYUP; } @@ -3948,38 +4051,37 @@ for this message. */ XXX We do not handle queue-only, freezing, or blackholes. */ -cutthrough_done = 0; -if(cutthrough_fd >= 0) +if(cutthrough.fd >= 0) { - uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the messsage */ + uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the message */ /* Logging was done in finaldot() */ switch(msg[0]) { case '2': /* Accept. Do the same to the source; dump any spoolfiles. */ - cutthrough_done = 3; + cutthrough_done = ACCEPTED; 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 */ + 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 */ - cutthrough_done = 2; + cutthrough_done = PERM_REJ; break; } } -if(smtp_reply == NULL -#ifdef EXPERIMENTAL_PRDR - || prdr_requested +#ifndef DISABLE_PRDR +if(!smtp_reply || prdr_requested) +#else +if(!smtp_reply) #endif - ) { log_write(0, LOG_MAIN | - (((log_extra_selector & LX_received_recipients) != 0)? LOG_RECIPIENTS : 0) | - (((log_extra_selector & LX_received_sender) != 0)? LOG_SENDER : 0), + (LOGGING(received_recipients)? LOG_RECIPIENTS : 0) | + (LOGGING(received_sender)? LOG_SENDER : 0), "%s", s); /* Log any control actions taken by an ACL or local_scan(). */ @@ -4054,14 +4156,21 @@ if (smtp_input) { uschar *code = US"250"; int len = 3; - smtp_message_code(&code, &len, &user_msg, NULL); + smtp_message_code(&code, &len, &user_msg, NULL, TRUE); smtp_respond(code, len, TRUE, user_msg); } /* Default OK response */ + else if (chunking_state > CHUNKING_OFFERED) + { + 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; + } else smtp_printf("250 OK id=%s\r\n", message_id); + if (host_checking) fprintf(stdout, "\n**** SMTP testing: that is not a real message id!\n\n"); @@ -4080,22 +4189,17 @@ if (smtp_input) 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 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 1: message_id[0] = 0; /* Prevent a delivery from starting */ + case TMP_REJ: message_id[0] = 0; /* Prevent a delivery from starting */ default:break; } - cutthrough_delivery = FALSE; + cutthrough.delivery = FALSE; } /* For batched SMTP, generate an error message on failure, and do @@ -4113,9 +4217,9 @@ starting. */ if (blackholed_by != NULL) { - uschar *detail = (local_scan_data != NULL)? - string_printing(local_scan_data) : - string_sprintf("(%s discarded recipients)", blackholed_by); + const uschar *detail = local_scan_data + ? string_printing(local_scan_data) + : string_sprintf("(%s discarded recipients)", blackholed_by); log_write(0, LOG_MAIN, "=> blackhole %s%s", detail, blackhole_log_msg); log_write(0, LOG_MAIN, "Completed"); message_id[0] = 0;