#endif
#ifdef SUPPORT_DMARC
-# include "dmarc.h"
+# include "miscmods/dmarc.h"
#endif
/*************************************************
BOOL
receive_check_fs(int msg_size)
{
-int_eximarith_t space;
int inodes;
if (check_spool_space > 0 || msg_size > 0 || check_spool_inodes > 0)
{
- space = receive_statvfs(TRUE, &inodes);
+ int_eximarith_t space = receive_statvfs(TRUE, &inodes);
DEBUG(D_receive)
debug_printf("spool directory space = " PR_EXIM_ARITH "K inodes = %d "
if (check_log_space > 0 || check_log_inodes > 0)
{
- space = receive_statvfs(FALSE, &inodes);
+ int_eximarith_t space = receive_statvfs(FALSE, &inodes);
DEBUG(D_receive)
debug_printf("log directory space = " PR_EXIM_ARITH "K inodes = %d "
well, so that there are no CRs in spooled messages. However, the message
terminating dot is not recognized between two bare CRs.
+Dec 2023: getting a site to send a body including an "LF . LF" sequence
+followed by SMTP commands is a possible "smtp smuggling" attack. If
+the first (header) line for the message has a proper CRLF then enforce
+that for the body: convert bare LF to a space.
+
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
+ strict_crlf require full CRLF sequence as a line ending
Returns: One of the END_xxx values indicating why it stopped reading
*/
static int
-read_message_data_smtp(FILE *fout)
+read_message_data_smtp(FILE * fout, BOOL strict_crlf)
{
-int ch_state = 0;
-int ch;
-int linelength = 0;
+enum { s_linestart, s_normal, s_had_cr, s_had_nl_dot, s_had_dot_cr } ch_state =
+ s_linestart;
+int linelength = 0, ch;
while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
{
if (ch == 0) body_zerocount++;
switch (ch_state)
{
- case 0: /* After LF or CRLF */
- if (ch == '.')
- {
- ch_state = 3;
- continue; /* Don't ever write . after LF */
- }
- ch_state = 1;
+ case s_linestart: /* After LF or CRLF */
+ if (ch == '.')
+ {
+ ch_state = s_had_nl_dot;
+ continue; /* Don't ever write . after LF */
+ }
+ ch_state = s_normal;
- /* Else fall through to handle as normal uschar. */
+ /* Else fall through to handle as normal uschar. */
- case 1: /* Normal state */
- if (ch == '\n')
- {
- ch_state = 0;
- body_linecount++;
+ case s_normal: /* Normal state */
+ if (ch == '\r')
+ {
+ ch_state = s_had_cr;
+ continue; /* Don't write the CR */
+ }
+ if (ch == '\n') /* Bare LF at end of line */
+ if (strict_crlf)
+ ch = ' '; /* replace LF with space */
+ else
+ { /* treat as line ending */
+ ch_state = s_linestart;
+ body_linecount++;
+ if (linelength > max_received_linelength)
+ max_received_linelength = linelength;
+ linelength = -1;
+ }
+ break;
+
+ case s_had_cr: /* After (unwritten) CR */
+ body_linecount++; /* Any char ends line */
if (linelength > max_received_linelength)
- max_received_linelength = linelength;
+ max_received_linelength = linelength;
linelength = -1;
- }
- else if (ch == '\r')
- {
- ch_state = 2;
- continue;
- }
- break;
+ if (ch == '\n') /* proper CRLF */
+ ch_state = s_linestart;
+ else
+ {
+ message_size++; /* convert the dropped CR to a stored NL */
+ if (fout && fputc('\n', fout) == EOF) return END_WERROR;
+ cutthrough_data_put_nl();
+ if (ch == '\r') /* CR; do not write */
+ continue;
+ ch_state = s_normal; /* not LF or CR; process as standard */
+ }
+ break;
- case 2: /* After (unwritten) CR */
- body_linecount++;
- if (linelength > max_received_linelength)
- max_received_linelength = linelength;
- linelength = -1;
- if (ch == '\n')
- {
- ch_state = 0;
- }
- else
- {
- message_size++;
- if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR;
- cutthrough_data_put_nl();
- if (ch != '\r') ch_state = 1; else continue;
- }
- break;
+ case s_had_nl_dot: /* After [CR] LF . */
+ if (ch == '\n') /* [CR] LF . LF */
+ if (strict_crlf)
+ ch = ' '; /* replace LF with space */
+ else
+ return END_DOT;
+ else if (ch == '\r') /* [CR] LF . CR */
+ {
+ ch_state = s_had_dot_cr;
+ continue; /* Don't write the CR */
+ }
+ /* The dot was removed on reaching s_had_nl_dot. For a doubled dot, here,
+ reinstate it to cutthrough. The current ch, dot or not, is passed both to
+ cutthrough and to file below. */
+ else if (ch == '.')
+ {
+ uschar c = ch;
+ cutthrough_data_puts(&c, 1);
+ }
+ ch_state = s_normal;
+ break;
- case 3: /* After [CR] LF . */
- if (ch == '\n')
- return END_DOT;
- if (ch == '\r')
- {
- ch_state = 4;
- continue;
- }
- /* 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;
- cutthrough_data_puts(&c, 1);
- }
- ch_state = 1;
- break;
+ case s_had_dot_cr: /* After [CR] LF . CR */
+ if (ch == '\n')
+ return END_DOT; /* Preferred termination */
- case 4: /* After [CR] LF . CR */
- if (ch == '\n') return END_DOT;
- message_size++;
- body_linecount++;
- if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR;
- cutthrough_data_put_nl();
- if (ch == '\r')
- {
- ch_state = 2;
- continue;
- }
- ch_state = 1;
- break;
+ message_size++; /* convert the dropped CR to a stored NL */
+ body_linecount++;
+ if (fout && fputc('\n', fout) == EOF) return END_WERROR;
+ cutthrough_data_put_nl();
+ if (ch == '\r')
+ {
+ ch_state = s_had_cr;
+ continue; /* CR; do not write */
+ }
+ ch_state = s_normal;
+ break;
}
/* Add the character to the spool file, unless skipping; then loop for the
{
if (message_ended >= END_NOTENDED)
message_ended = chunking_state <= CHUNKING_OFFERED
- ? read_message_data_smtp(NULL)
+ ? read_message_data_smtp(NULL, FALSE)
: read_message_bdat_smtp_wire(NULL);
}
give_local_error(int errcode, uschar *text1, uschar *text2, int error_rc,
FILE *f, header_line *hptr)
{
+DEBUG(D_all) debug_printf("%s%s\n", text2, text1);
+
if (error_handling == ERRORS_SENDER)
{
error_block eblock;
if (sender_ident)
g = string_append(g, 2, US" U=", sender_ident);
if (LOGGING(connection_id))
- g = string_fmt_append(g, " Ci=%lu", connection_id);
+ g = string_fmt_append(g, " Ci=%s", connection_id);
if (received_protocol)
g = string_append(g, 2, US" P=", received_protocol);
if (LOGGING(pipelining) && f.smtp_in_pipelining_advertised)
header_line * received_header= header_list;
if (recipients_count == 1) received_for = recipients_list[0].address;
+GET_OPTION("received_header_text");
received = expand_string(received_header_text);
received_for = NULL;
/* Working header pointers */
rmark reset_point;
-header_line *next;
+header_line * next;
/* Flags for noting the existence of certain headers (only one left) */
/* Pointers to receive the addresses of headers whose contents we need. */
-header_line *from_header = NULL;
-header_line *subject_header = NULL;
-header_line *msgid_header = NULL;
-header_line *received_header;
+header_line * from_header = NULL;
+#ifdef SUPPORT_DMARC
+header_line * dmarc_from_header = NULL;
+#endif
+header_line * subject_header = NULL, * msgid_header = NULL, * received_header;
BOOL msgid_header_newly_created = FALSE;
/* Variables for use when building the Received: header. */
-uschar *timestamp;
+uschar * timestamp;
int tslen;
/* Time of creation of message_id */
mime_part_count = -1;
#endif
-#ifndef DISABLE_DKIM
-/* Call into DKIM to set up the context. In CHUNKING mode
-we clear the dot-stuffing flag */
-if (smtp_input && !smtp_batched_input && !f.dkim_disable_verify)
- dkim_exim_verify_init(chunking_state <= CHUNKING_OFFERED);
-#endif
-
-#ifdef SUPPORT_DMARC
-if (sender_host_address) dmarc_init(); /* initialize libopendmarc */
-#endif
+if (misc_mod_msg_init() != OK)
+ goto TIDYUP;
/* In SMTP sessions we may receive several messages in one connection. Before
each subsequent one, we wait for the clock to tick at the level of message-id
if (ch == '\n')
{
- if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = FALSE;
- else if (first_line_ended_crlf) receive_ungetc(' ');
+ if (first_line_ended_crlf == TRUE_UNSET)
+ first_line_ended_crlf = FALSE;
+ else if (first_line_ended_crlf)
+ receive_ungetc(' ');
goto EOL;
}
This implements the dot-doubling rule, though header lines starting with
dots aren't exactly common. They are legal in RFC 822, though. If the
following is CRLF or LF, this is the line that that terminates the
+
entire message. We set message_ended to indicate this has happened (to
prevent further reading), and break out of the loop, having freed the
empty header, and set next = NULL to indicate no data line. */
if (f.dot_ends && ptr == 0 && ch == '.')
{
+ /* leading dot while in headers-read mode */
ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
- if (ch == '\r')
+ if (ch == '\n' && first_line_ended_crlf == TRUE /* and not TRUE_UNSET */ )
+ /* dot, LF but we are in CRLF mode. Attack? */
+ ch = ' '; /* replace the LF with a space */
+
+ else if (ch == '\r')
{
ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
if (ch != '\n')
ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
if (ch == '\n')
{
- if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = TRUE;
+ if (first_line_ended_crlf == TRUE_UNSET)
+ first_line_ended_crlf = TRUE;
goto EOL;
}
{
if (!f.sender_address_forced)
{
- uschar *uucp_sender = expand_string(uucp_from_sender);
- if (!uucp_sender)
+ uschar * uucp_sender;
+ GET_OPTION("uucp_from_sender");
+ if (!(uucp_sender = expand_string(uucp_from_sender)))
log_write(0, LOG_MAIN|LOG_PANIC,
"expansion of \"%s\" failed after matching "
"\"From \" line: %s", uucp_from_sender, expand_string_message);
if (isspace(*p)) break;
while (mac_isgraph(*p) && *p != ':') p++;
- while (isspace(*p)) p++;
- if (*p != ':')
+ if (Uskip_whitespace(&p) != ':')
{
body_zerocount = had_zero;
break;
DEBUG(D_receive)
{
debug_printf(">>Headers received:\n");
+ acl_level++;
for (header_line * h = header_list->next; h; h = h->next)
- debug_printf("%s", h->text);
+ debug_printf_indent("%s", h->text);
debug_printf("\n");
+ acl_level--;
}
/* End of file on any SMTP connection is an error. If an incoming SMTP call
case htype_from:
h->type = htype_from;
+#ifdef SUPPORT_DMARC
+ if (!is_resent) dmarc_from_header = h;
+#endif
if (!resents_exist || is_resent)
{
from_header = h;
if (!smtp_input)
{
int len;
- uschar *s = Ustrchr(h->text, ':') + 1;
- while (isspace(*s)) s++;
+ uschar * s = Ustrchr(h->text, ':') + 1;
+
+ Uskip_whitespace(&s);
len = h->slen - (s - h->text) - 1;
if (Ustrlen(originator_login) == len &&
strncmpic(s, originator_login, len) == 0)
{
- uschar *name = is_resent? US"Resent-From" : US"From";
+ uschar * name = is_resent ? US"Resent-From" : US"From";
header_add(htype_from, "%s: %s <%s@%s>\n", name, originator_name,
originator_login, qualify_domain_sender);
from_header = header_last;
if (filter_test != FTEST_NONE)
{
- uschar *start = h->text + 12;
- uschar *end = start + Ustrlen(start);
- while (isspace(*start)) start++;
+ uschar * start = h->text + 12;
+ uschar * end = start + Ustrlen(start);
+
+ Uskip_whitespace(&start);
while (end > start && isspace(end[-1])) end--;
if (*start == '<' && end[-1] == '>')
- {
- start++;
- end--;
- }
+ { start++; end--; }
return_path = string_copyn(start, end - start);
printf("Return-path taken from \"Return-path:\" header line\n");
}
if ((h->type == htype_to || h->type == htype_cc || h->type == htype_bcc) &&
(!contains_resent_headers || strncmpic(h->text, US"resent-", 7) == 0))
{
- uschar *s = Ustrchr(h->text, ':') + 1;
- while (isspace(*s)) s++;
+ uschar * s = Ustrchr(h->text, ':') + 1;
+ Uskip_whitespace(&s);
f.parse_allow_group = TRUE; /* Allow address group syntax */
- while (*s != 0)
+ while (*s)
{
uschar *ss = parse_find_address_end(s, FALSE);
uschar *recipient, *errmess, *pp;
/* Check on maximum */
- if (recipients_max > 0 && ++rcount > recipients_max)
+ if (recipients_max_expanded > 0 && ++rcount > recipients_max_expanded)
give_local_error(ERRMESS_TOOMANYRECIP, US"too many recipients",
US"message rejected: ", error_rc, stdin, NULL);
/* Does not return */
/* Move on past this address */
s = ss + (*ss ? 1 : 0);
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
} /* Next address */
f.parse_allow_group = FALSE; /* Reset group syntax flags */
/* Permit only letters, digits, dots, and hyphens in the domain */
+ GET_OPTION("message_id_header_domain");
if (message_id_domain)
{
uschar *new_id_domain = expand_string(message_id_domain);
/* Permit all characters except controls and RFC 2822 specials in the
additional text part. */
+ GET_OPTION("message_id_header_text");
if (message_id_text)
{
uschar *new_id_text = expand_string(message_id_text);
it has already been rewritten as part of verification for SMTP input. */
DEBUG(D_rewrite)
- { debug_printf("global rewrite rules\n"); acl_level++; }
+ { debug_printf("rewrite rules on sender address\n"); acl_level++; }
if (global_rewrite_rules && !sender_address_unrewritten && *sender_address)
{
/* deconst ok as src was not const */
by the -t command line option. An added Sender: gets rewritten here. */
DEBUG(D_rewrite)
- { debug_printf("rewrite headers\n"); acl_level++; }
+ { debug_printf("qualify and rewrite headers\n"); acl_level++; }
for (header_line * h = header_list->next, * newh; h; h = h->next)
if ((newh = rewrite_header(h, NULL, NULL, global_rewrite_rules,
rewrite_existflags, TRUE)))
DEBUG(D_receive)
{
debug_printf(">>Headers after rewriting and local additions:\n");
+ acl_level++;
for (header_line * h = header_list->next; h; h = h->next)
- debug_printf("%c %s", h->type, h->text);
+ debug_printf_indent("%c %s", h->type, h->text);
debug_printf("\n");
+ acl_level--;
}
/* The headers are now complete in store. If we are running in filter
/* 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
+to access it both via a file descriptor and a stdio stream. Try to make the
directory if it isn't there. */
spool_name = spool_fname(US"input", message_subdir, message_id, US"-D");
if (smtp_input)
{
message_ended = chunking_state <= CHUNKING_OFFERED
- ? read_message_data_smtp(spool_data_file)
+ ? read_message_data_smtp(spool_data_file, first_line_ended_crlf)
: spool_wireformat
? read_message_bdat_smtp_wire(spool_data_file)
: read_message_bdat_smtp(spool_data_file);
#ifndef DISABLE_DKIM
if (!f.dkim_disable_verify)
{
- /* Finish verification */
- dkim_exim_verify_finish();
+ misc_module_info * mi = misc_mod_findonly(US"dkim");
+ if (mi)
+ {
+ typedef void (*vfin_fn_t)(void);
+ typedef int (*vacl_fn_t)(uschar **, uschar**);
+ typedef void (*vlog_fn_t)(void);
- /* Check if we must run the DKIM ACL */
- if (acl_smtp_dkim && dkim_verify_signers && *dkim_verify_signers)
- {
- uschar * dkim_verify_signers_expanded =
- expand_string(dkim_verify_signers);
- gstring * results = NULL;
- int signer_sep = 0;
- const uschar * ptr;
- uschar * item;
- gstring * seen_items = NULL;
- int old_pool = store_pool;
-
- store_pool = POOL_PERM; /* Allow created variables to live to data ACL */
-
- if (!(ptr = dkim_verify_signers_expanded))
- log_write(0, LOG_MAIN|LOG_PANIC,
- "expansion of dkim_verify_signers option failed: %s",
- expand_string_message);
-
- /* Default to OK when no items are present */
- rc = OK;
- while ((item = string_nextinlist(&ptr, &signer_sep, NULL, 0)))
- {
- /* Prevent running ACL for an empty item */
- if (!item || !*item) continue;
+ /* Finish off the body hashes, calculate sigs and do compares */
- /* 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;
- const uschar * seen_items_list = string_from_gstring(seen_items);
- int seen_sep = ':';
- BOOL seen_this_item = FALSE;
-
- while ((seen_item = string_nextinlist(&seen_items_list, &seen_sep,
- 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);
- continue;
- }
+ (((vfin_fn_t *) mi->functions)[DKIM_VERIFY_FINISH]) ();
- seen_items = string_catn(seen_items, US":", 1);
- }
- seen_items = string_cat(seen_items, item);
+ /* Check if we must run the DKIM ACL */
+
+ GET_OPTION("acl_smtp_dkim");
+ if (acl_smtp_dkim)
+ {
+ rc = (((vacl_fn_t *) mi->functions)[DKIM_ACL_ENTRY])
+ (&user_msg, &log_msg);
+ add_acl_headers(ACL_WHERE_DKIM, US"DKIM");
- rc = dkim_exim_acl_run(item, &results, &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(TRUE, US"dkim acl not ok");
- break;
+
+ if (rc != DISCARD)
+ {
+ Uunlink(spool_name);
+ if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0)
+ smtp_yield = FALSE; /* No more msgs after dropped conn */
+ smtp_reply = US""; /* Indicate reply already sent */
+ goto NOT_ACCEPTED; /* Skip to end of function */
+ }
+ recipients_count = 0;
+ blackholed_by = US"DKIM ACL";
+ if (log_msg)
+ blackhole_log_msg = string_sprintf(": %s", log_msg);
}
}
- dkim_verify_status = string_from_gstring(results);
- store_pool = old_pool;
- add_acl_headers(ACL_WHERE_DKIM, US"DKIM");
- if (rc == DISCARD)
- {
- recipients_count = 0;
- blackholed_by = US"DKIM ACL";
- if (log_msg)
- blackhole_log_msg = string_sprintf(": %s", log_msg);
- }
- else if (rc != OK)
- {
- Uunlink(spool_name);
- if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0)
- smtp_yield = FALSE; /* No more messages after dropped connection */
- smtp_reply = US""; /* Indicate reply already sent */
- goto NOT_ACCEPTED; /* Skip to end of function */
- }
- }
- else
- dkim_exim_verify_log_all();
+ else /* No ACL; just log */
+ (((vlog_fn_t *) mi->functions)[DKIM_VERIFY_LOG_ALL]) ();
+ }
}
#endif /* DISABLE_DKIM */
#ifdef WITH_CONTENT_SCAN
- if ( recipients_count > 0
- && acl_smtp_mime
- && !run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by)
- )
- goto TIDYUP;
+ if (recipients_count > 0)
+ {
+ GET_OPTION("acl_smtp_mime");
+ if (acl_smtp_mime
+ && !run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by)
+ )
+ goto TIDYUP;
+ }
#endif /* WITH_CONTENT_SCAN */
#ifdef SUPPORT_DMARC
- dmarc_store_data(from_header);
+ {
+ misc_module_info * mi = misc_mod_findonly(US"dmarc");
+ if (mi)
+ {
+ typedef int (*fn_t)(header_line *);
+ (((fn_t *) mi->functions)[DMARC_STORE_DATA]) (dmarc_from_header);
+ }
+ }
#endif
#ifndef DISABLE_PRDR
- if (prdr_requested && recipients_count > 1 && acl_smtp_data_prdr)
+ if (prdr_requested && recipients_count > 1)
{
- int all_pass = OK;
- int all_fail = FAIL;
+ GET_OPTION("acl_smtp_data_prdr");
+ if (acl_smtp_data_prdr)
+ {
+ int all_pass = OK;
+ int all_fail = FAIL;
- smtp_printf("353 PRDR content analysis beginning\r\n", SP_MORE);
- /* Loop through recipients, responses must be in same order received */
- for (unsigned int c = 0; recipients_count > c; c++)
- {
- const uschar * addr = recipients_list[c].address;
- uschar * msg= US"PRDR R=<%s> %s";
- uschar * code;
- DEBUG(D_receive)
- debug_printf("PRDR processing recipient %s (%d of %d)\n",
- addr, c+1, recipients_count);
- rc = acl_check(ACL_WHERE_PRDR, addr,
- acl_smtp_data_prdr, &user_msg, &log_msg);
-
- /* If any recipient rejected content, indicate it in final message */
- all_pass |= rc;
- /* If all recipients rejected, indicate in final message */
- all_fail &= rc;
-
- switch (rc)
- {
- case OK: case DISCARD: code = US"250"; break;
- case DEFER: code = US"450"; break;
- default: code = US"550"; break;
- }
- if (user_msg != NULL)
- smtp_user_msg(code, user_msg);
- else
+ smtp_printf("353 PRDR content analysis beginning\r\n", SP_MORE);
+ /* Loop through recipients, responses must be in same order received */
+ for (unsigned int c = 0; recipients_count > c; c++)
{
+ const uschar * addr = recipients_list[c].address;
+ uschar * msg= US"PRDR R=<%s> %s";
+ uschar * code;
+ DEBUG(D_receive)
+ debug_printf("PRDR processing recipient %s (%d of %d)\n",
+ addr, c+1, recipients_count);
+ rc = acl_check(ACL_WHERE_PRDR, addr,
+ acl_smtp_data_prdr, &user_msg, &log_msg);
+
+ /* If any recipient rejected content, indicate it in final message */
+ all_pass |= rc;
+ /* If all recipients rejected, indicate in final message */
+ all_fail &= rc;
+
switch (rc)
- {
- case OK: case DISCARD:
- msg = string_sprintf(CS msg, addr, "acceptance"); break;
- case DEFER:
- msg = string_sprintf(CS msg, addr, "temporary refusal"); break;
- default:
- msg = string_sprintf(CS msg, addr, "refusal"); break;
- }
- smtp_user_msg(code, msg);
- }
- 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, "%s", CS msg);
+ {
+ case OK: case DISCARD: code = US"250"; break;
+ case DEFER: code = US"450"; break;
+ default: code = US"550"; break;
+ }
+ if (user_msg != NULL)
+ smtp_user_msg(code, user_msg);
+ else
+ {
+ switch (rc)
+ {
+ case OK: case DISCARD:
+ msg = string_sprintf(CS msg, addr, "acceptance"); break;
+ case DEFER:
+ msg = string_sprintf(CS msg, addr, "temporary refusal"); break;
+ default:
+ msg = string_sprintf(CS msg, addr, "refusal"); break;
+ }
+ smtp_user_msg(code, msg);
+ }
+ 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, "%s", CS msg);
- if (rc != OK) { receive_remove_recipient(addr); c--; }
- }
- /* Set up final message, used if data acl gives OK */
- smtp_reply = string_sprintf("%s id=%s message %s",
- all_fail == FAIL ? US"550" : US"250",
- message_id,
- all_fail == FAIL
- ? US"rejected for all recipients"
- : all_pass == OK
- ? US"accepted"
- : US"accepted for some recipients");
- if (recipients_count == 0)
- goto NOT_ACCEPTED;
+ if (rc != OK) { receive_remove_recipient(addr); c--; }
+ }
+ /* Set up final message, used if data acl gives OK */
+ smtp_reply = string_sprintf("%s id=%s message %s",
+ all_fail == FAIL ? US"550" : US"250",
+ message_id,
+ all_fail == FAIL
+ ? US"rejected for all recipients"
+ : all_pass == OK
+ ? US"accepted"
+ : US"accepted for some recipients");
+ if (recipients_count == 0)
+ goto NOT_ACCEPTED;
+ }
+ else
+ prdr_requested = FALSE;
}
else
prdr_requested = FALSE;
/* Check the recipients count again, as the MIME ACL might have changed
them. */
+ GET_OPTION("acl_smtp_data");
if (acl_smtp_data && recipients_count > 0)
{
rc = acl_check(ACL_WHERE_DATA, NULL, acl_smtp_data, &user_msg, &log_msg);
{
#ifdef WITH_CONTENT_SCAN
+ GET_OPTION("acl_not_smtp_mime");
if ( acl_not_smtp_mime
&& !run_mime_acl(acl_not_smtp_mime, &smtp_yield, &smtp_reply,
&blackholed_by)
goto TIDYUP;
#endif /* WITH_CONTENT_SCAN */
+ GET_OPTION("acl_not_smtp");
if (acl_not_smtp)
{
- uschar *user_msg, *log_msg;
+ uschar * user_msg, * log_msg;
f.authentication_local = TRUE;
rc = acl_check(ACL_WHERE_NOTSMTP, NULL, acl_not_smtp, &user_msg, &log_msg);
if (rc == DISCARD)
receive_messagecount++;
-if (fflush(spool_data_file))
+if ( fflush(spool_data_file)
+#if _POSIX_C_SOURCE >= 199309L || _XOPEN_SOURCE >= 500
+# ifdef ENABLE_DISABLE_FSYNC
+ || !disable_fsync && fdatasync(data_fd)
+# else
+ || fdatasync(data_fd)
+# endif
+#endif
+ )
{
errmsg = string_sprintf("Spool write error: %s", strerror(errno));
log_write(0, LOG_MAIN, "%s\n", errmsg);
g = string_fmt_append(g, " M8S=%d", body_8bitmime);
#ifndef DISABLE_DKIM
-if (LOGGING(dkim) && dkim_verify_overall)
- g = string_append(g, 2, US" DKIM=", dkim_verify_overall);
+if (LOGGING(dkim))
+ {
+ misc_module_info * mi = misc_mod_findonly(US"dkim");
+ typedef gstring * (*fn_t)(gstring *);
+ if (mi)
+ g = (((fn_t *) mi->functions)[DKIM_VDOM_FIRSTPASS]) (g);
+
# ifdef EXPERIMENTAL_ARC
-if (LOGGING(dkim) && arc_state && Ustrcmp(arc_state, "pass") == 0)
- g = string_catn(g, US" ARC", 4);
+ {
+ mi = misc_mod_findonly(US"arc");
+ typedef BOOL (*fn_t)(void);
+ if (mi && (((fn_t *) mi->functions)[ARC_STATE_IS_PASS]) ())
+ g = string_catn(g, US" ARC", 4);
+ }
# endif
+ }
#endif
if (LOGGING(receive_time))
}
/* End of receive.c */
+/* vi: se aw ai sw=2
+*/