-/* $Cambridge: exim/src/src/acl.c,v 1.44 2005/08/22 14:01:37 ph10 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.75 2007/03/01 11:17:00 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
/* See the file NOTICE for conditions of use and distribution. */
/* Code for handling Access Control Lists (ACLs) */
{ US"accept", US"defer", US"deny", US"discard", US"drop", US"require",
US"warn" };
-/* For each verb, the condition for which "message" is used */
-
-static int msgcond[] = { FAIL, OK, OK, FAIL, OK, FAIL, OK };
+/* For each verb, the conditions for which "message" or "log_message" are used
+are held as a bitmap. This is to avoid expanding the strings unnecessarily. For
+"accept", the FAIL case is used only after "endpass", but that is selected in
+the code. */
+
+static int msgcond[] = {
+ (1<<OK) | (1<<FAIL) | (1<<FAIL_DROP), /* accept */
+ (1<<OK), /* defer */
+ (1<<OK), /* deny */
+ (1<<OK) | (1<<FAIL) | (1<<FAIL_DROP), /* discard */
+ (1<<OK), /* drop */
+ (1<<FAIL) | (1<<FAIL_DROP), /* require */
+ (1<<OK) /* warn */
+ };
/* ACL condition and modifier codes - keep in step with the table that
-follows. */
+follows, and the cond_expand_at_top and uschar cond_modifiers tables lower
+down. */
-enum { ACLC_ACL, ACLC_AUTHENTICATED,
+enum { ACLC_ACL,
+ ACLC_ADD_HEADER,
+ ACLC_AUTHENTICATED,
#ifdef EXPERIMENTAL_BRIGHTMAIL
ACLC_BMI_OPTIN,
#endif
-ACLC_CONDITION, ACLC_CONTROL,
+ ACLC_CONDITION,
+ ACLC_CONTINUE,
+ ACLC_CONTROL,
#ifdef WITH_CONTENT_SCAN
ACLC_DECODE,
#endif
ACLC_DK_SENDERS,
ACLC_DK_STATUS,
#endif
- ACLC_DNSLISTS, ACLC_DOMAINS, ACLC_ENCRYPTED, ACLC_ENDPASS,
- ACLC_HOSTS, ACLC_LOCAL_PARTS, ACLC_LOG_MESSAGE, ACLC_LOGWRITE,
+ ACLC_DNSLISTS,
+ ACLC_DOMAINS,
+ ACLC_ENCRYPTED,
+ ACLC_ENDPASS,
+ ACLC_HOSTS,
+ ACLC_LOCAL_PARTS,
+ ACLC_LOG_MESSAGE,
+ ACLC_LOG_REJECT_TARGET,
+ ACLC_LOGWRITE,
#ifdef WITH_CONTENT_SCAN
ACLC_MALWARE,
#endif
#ifdef WITH_CONTENT_SCAN
ACLC_REGEX,
#endif
- ACLC_SENDER_DOMAINS, ACLC_SENDERS, ACLC_SET,
+ ACLC_SENDER_DOMAINS,
+ ACLC_SENDERS,
+ ACLC_SET,
#ifdef WITH_CONTENT_SCAN
ACLC_SPAM,
#endif
#endif
ACLC_VERIFY };
-/* ACL conditions/modifiers: "delay", "control", "endpass", "message",
-"log_message", "logwrite", and "set" are modifiers that look like conditions
-but always return TRUE. They are used for their side effects. */
+/* ACL conditions/modifiers: "delay", "control", "continue", "endpass",
+"message", "log_message", "log_reject_target", "logwrite", and "set" are
+modifiers that look like conditions but always return TRUE. They are used for
+their side effects. */
static uschar *conditions[] = {
US"acl",
+ US"add_header",
US"authenticated",
#ifdef EXPERIMENTAL_BRIGHTMAIL
US"bmi_optin",
#endif
US"condition",
+ US"continue",
US"control",
#ifdef WITH_CONTENT_SCAN
US"decode",
US"dk_senders",
US"dk_status",
#endif
- US"dnslists", US"domains", US"encrypted",
- US"endpass", US"hosts", US"local_parts", US"log_message", US"logwrite",
+ US"dnslists",
+ US"domains",
+ US"encrypted",
+ US"endpass",
+ US"hosts",
+ US"local_parts",
+ US"log_message",
+ US"log_reject_target",
+ US"logwrite",
#ifdef WITH_CONTENT_SCAN
US"malware",
#endif
that follows! */
enum {
-#ifdef EXPERIMENTAL_BRIGHTMAIL
+ CONTROL_AUTH_UNADVERTISED,
+ #ifdef EXPERIMENTAL_BRIGHTMAIL
CONTROL_BMI_RUN,
-#endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
+ #endif
+ #ifdef EXPERIMENTAL_DOMAINKEYS
CONTROL_DK_VERIFY,
-#endif
- CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, CONTROL_CASELOWER_LOCAL_PART,
- CONTROL_ENFORCE_SYNC, CONTROL_NO_ENFORCE_SYNC, CONTROL_FREEZE,
- CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION,
-#ifdef WITH_CONTENT_SCAN
+ #endif
+ CONTROL_ERROR,
+ CONTROL_CASEFUL_LOCAL_PART,
+ CONTROL_CASELOWER_LOCAL_PART,
+ CONTROL_ENFORCE_SYNC,
+ CONTROL_NO_ENFORCE_SYNC,
+ CONTROL_FREEZE,
+ CONTROL_QUEUE_ONLY,
+ CONTROL_SUBMISSION,
+ CONTROL_SUPPRESS_LOCAL_FIXUPS,
+ #ifdef WITH_CONTENT_SCAN
CONTROL_NO_MBOX_UNSPOOL,
-#endif
- CONTROL_FAKEDEFER, CONTROL_FAKEREJECT, CONTROL_NO_MULTILINE };
+ #endif
+ CONTROL_FAKEDEFER,
+ CONTROL_FAKEREJECT,
+ CONTROL_NO_MULTILINE,
+ CONTROL_NO_PIPELINING,
+ CONTROL_NO_DELAY_FLUSH,
+ CONTROL_NO_CALLOUT_FLUSH
+};
-/* ACL control names; keep in step with the table above! */
+/* ACL control names; keep in step with the table above! This list is used for
+turning ids into names. The actual list of recognized names is in the variable
+control_def controls_list[] below. The fact that there are two lists is a mess
+and should be tidied up. */
static uschar *controls[] = {
+ US"allow_auth_unadvertised",
#ifdef EXPERIMENTAL_BRIGHTMAIL
US"bmi_run",
#endif
#ifdef EXPERIMENTAL_DOMAINKEYS
US"dk_verify",
#endif
- US"error", US"caseful_local_part",
- US"caselower_local_part", US"enforce_sync", US"no_enforce_sync", US"freeze",
- US"queue_only", US"submission",
+ US"error",
+ US"caseful_local_part",
+ US"caselower_local_part",
+ US"enforce_sync",
+ US"no_enforce_sync",
+ US"freeze",
+ US"queue_only",
+ US"submission",
+ US"suppress_local_fixups",
#ifdef WITH_CONTENT_SCAN
US"no_mbox_unspool",
#endif
- US"no_multiline"};
+ US"fakedefer",
+ US"fakereject",
+ US"no_multiline",
+ US"no_pipelining",
+ US"no_delay_flush",
+ US"no_callout_flush"
+};
-/* Flags to indicate for which conditions /modifiers a string expansion is done
+/* Flags to indicate for which conditions/modifiers a string expansion is done
at the outer level. In the other cases, expansion already occurs in the
checking functions. */
static uschar cond_expand_at_top[] = {
TRUE, /* acl */
+ TRUE, /* add_header */
FALSE, /* authenticated */
#ifdef EXPERIMENTAL_BRIGHTMAIL
TRUE, /* bmi_optin */
#endif
TRUE, /* condition */
+ TRUE, /* continue */
TRUE, /* control */
#ifdef WITH_CONTENT_SCAN
TRUE, /* decode */
FALSE, /* hosts */
FALSE, /* local_parts */
TRUE, /* log_message */
+ TRUE, /* log_reject_target */
TRUE, /* logwrite */
#ifdef WITH_CONTENT_SCAN
TRUE, /* malware */
static uschar cond_modifiers[] = {
FALSE, /* acl */
+ TRUE, /* add_header */
FALSE, /* authenticated */
#ifdef EXPERIMENTAL_BRIGHTMAIL
TRUE, /* bmi_optin */
#endif
FALSE, /* condition */
+ TRUE, /* continue */
TRUE, /* control */
#ifdef WITH_CONTENT_SCAN
FALSE, /* decode */
FALSE, /* hosts */
FALSE, /* local_parts */
TRUE, /* log_message */
+ TRUE, /* log_reject_target */
TRUE, /* logwrite */
#ifdef WITH_CONTENT_SCAN
FALSE, /* malware */
FALSE /* verify */
};
-/* Bit map vector of which conditions are not allowed at certain times. For
-each condition, there's a bitmap of dis-allowed times. For some, it is easier
-to specify the negation of a small number of allowed times. */
+/* Bit map vector of which conditions and modifiers are not allowed at certain
+times. For each condition, there's a bitmap of dis-allowed times. For some, it
+is easier to specify the negation of a small number of allowed times. */
static unsigned int cond_forbids[] = {
0, /* acl */
- (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_CONNECT)| /* authenticated */
- (1<<ACL_WHERE_HELO),
+ (unsigned int)
+ ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* add_header */
+ (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ (1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
+ (1<<ACL_WHERE_NOTSMTP_START)),
-#ifdef EXPERIMENTAL_BRIGHTMAIL
+ (1<<ACL_WHERE_NOTSMTP)| /* authenticated */
+ (1<<ACL_WHERE_NOTSMTP_START)|
+ (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO),
+
+ #ifdef EXPERIMENTAL_BRIGHTMAIL
(1<<ACL_WHERE_AUTH)| /* bmi_optin */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_MIME)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
(1<<ACL_WHERE_MAILAUTH)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_PREDATA),
-#endif
+ (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_PREDATA)|
+ (1<<ACL_WHERE_NOTSMTP_START),
+ #endif
0, /* condition */
+ 0, /* continue */
+
/* Certain types of control are always allowed, so we let it through
always and check in the control processing itself. */
0, /* control */
-#ifdef WITH_CONTENT_SCAN
+ #ifdef WITH_CONTENT_SCAN
(unsigned int)
~(1<<ACL_WHERE_MIME), /* decode */
-#endif
+ #endif
0, /* delay */
-#ifdef WITH_OLD_DEMIME
+ #ifdef WITH_OLD_DEMIME
(unsigned int)
~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* demime */
-#endif
+ #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
+ #ifdef EXPERIMENTAL_DOMAINKEYS
(1<<ACL_WHERE_AUTH)| /* dk_domain_source */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
(1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY),
+ (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
(1<<ACL_WHERE_AUTH)| /* dk_policy */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
(1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY),
+ (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
(1<<ACL_WHERE_AUTH)| /* dk_sender_domains */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
(1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY),
+ (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
(1<<ACL_WHERE_AUTH)| /* dk_sender_local_parts */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
(1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY),
+ (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
(1<<ACL_WHERE_AUTH)| /* dk_senders */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
(1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY),
+ (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
(1<<ACL_WHERE_AUTH)| /* dk_status */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
(1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY),
-#endif
+ (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
+ #endif
- (1<<ACL_WHERE_NOTSMTP), /* dnslists */
+ (1<<ACL_WHERE_NOTSMTP)| /* dnslists */
+ (1<<ACL_WHERE_NOTSMTP_START),
(unsigned int)
~(1<<ACL_WHERE_RCPT), /* domains */
- (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_CONNECT)| /* encrypted */
+ (1<<ACL_WHERE_NOTSMTP)| /* encrypted */
+ (1<<ACL_WHERE_CONNECT)|
+ (1<<ACL_WHERE_NOTSMTP_START)|
(1<<ACL_WHERE_HELO),
0, /* endpass */
- (1<<ACL_WHERE_NOTSMTP), /* hosts */
+ (1<<ACL_WHERE_NOTSMTP)| /* hosts */
+ (1<<ACL_WHERE_NOTSMTP_START),
(unsigned int)
~(1<<ACL_WHERE_RCPT), /* local_parts */
0, /* log_message */
+ 0, /* log_reject_target */
+
0, /* logwrite */
-#ifdef WITH_CONTENT_SCAN
+ #ifdef WITH_CONTENT_SCAN
(unsigned int)
~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* malware */
-#endif
+ #endif
0, /* message */
-#ifdef WITH_CONTENT_SCAN
+ #ifdef WITH_CONTENT_SCAN
(unsigned int)
~(1<<ACL_WHERE_MIME), /* mime_regex */
-#endif
+ #endif
0, /* ratelimit */
(unsigned int)
~(1<<ACL_WHERE_RCPT), /* recipients */
-#ifdef WITH_CONTENT_SCAN
+ #ifdef WITH_CONTENT_SCAN
(unsigned int)
~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* regex */
(1<<ACL_WHERE_MIME)),
-#endif
+ #endif
(1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)| /* sender_domains */
(1<<ACL_WHERE_HELO)|
0, /* set */
-#ifdef WITH_CONTENT_SCAN
+ #ifdef WITH_CONTENT_SCAN
(unsigned int)
~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* spam */
-#endif
+ #endif
-#ifdef EXPERIMENTAL_SPF
+ #ifdef EXPERIMENTAL_SPF
(1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)| /* spf */
(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_MAILAUTH)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
- (1<<ACL_WHERE_STARTTLS)|(1<<ACL_WHERE_VRFY),
-#endif
+ (1<<ACL_WHERE_STARTTLS)|(1<<ACL_WHERE_VRFY)|
+ (1<<ACL_WHERE_NOTSMTP)|
+ (1<<ACL_WHERE_NOTSMTP_START),
+ #endif
/* Certain types of verify are always allowed, so we let it through
always and check in the verify function itself */
specify the negation of a small number of allowed times. */
static unsigned int control_forbids[] = {
-#ifdef EXPERIMENTAL_BRIGHTMAIL
+ (unsigned int)
+ ~((1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)), /* allow_auth_unadvertised */
+
+ #ifdef EXPERIMENTAL_BRIGHTMAIL
0, /* bmi_run */
-#endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
- (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP), /* dk_verify */
-#endif
+ #endif
+
+ #ifdef EXPERIMENTAL_DOMAINKEYS
+ (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dk_verify */
+ (1<<ACL_WHERE_NOTSMTP_START),
+ #endif
0, /* error */
(unsigned int)
~(1<<ACL_WHERE_RCPT), /* caselower_local_part */
- (1<<ACL_WHERE_NOTSMTP), /* enforce_sync */
+ (1<<ACL_WHERE_NOTSMTP)| /* enforce_sync */
+ (1<<ACL_WHERE_NOTSMTP_START),
- (1<<ACL_WHERE_NOTSMTP), /* no_enforce_sync */
+ (1<<ACL_WHERE_NOTSMTP)| /* no_enforce_sync */
+ (1<<ACL_WHERE_NOTSMTP_START),
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* freeze */
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* submission */
(1<<ACL_WHERE_PREDATA)),
-#ifdef WITH_CONTENT_SCAN
+ (unsigned int)
+ ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* suppress_local_fixups */
+ (1<<ACL_WHERE_PREDATA)|
+ (1<<ACL_WHERE_NOTSMTP_START)),
+
+ #ifdef WITH_CONTENT_SCAN
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* no_mbox_unspool */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
(1<<ACL_WHERE_MIME)),
-#endif
+ #endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakedefer */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
(1<<ACL_WHERE_MIME)),
- (1<<ACL_WHERE_NOTSMTP) /* no_multiline */
+ (1<<ACL_WHERE_NOTSMTP)| /* no_multiline */
+ (1<<ACL_WHERE_NOTSMTP_START),
+
+ (1<<ACL_WHERE_NOTSMTP)| /* no_pipelining */
+ (1<<ACL_WHERE_NOTSMTP_START),
+
+ (1<<ACL_WHERE_NOTSMTP)| /* no_delay_flush */
+ (1<<ACL_WHERE_NOTSMTP_START),
+
+ (1<<ACL_WHERE_NOTSMTP)| /* no_callout_flush */
+ (1<<ACL_WHERE_NOTSMTP_START)
};
/* Structure listing various control arguments, with their characteristics. */
} control_def;
static control_def controls_list[] = {
+ { US"allow_auth_unadvertised", CONTROL_AUTH_UNADVERTISED, FALSE },
#ifdef EXPERIMENTAL_BRIGHTMAIL
- { US"bmi_run", CONTROL_BMI_RUN, FALSE},
+ { US"bmi_run", CONTROL_BMI_RUN, FALSE },
#endif
#ifdef EXPERIMENTAL_DOMAINKEYS
- { US"dk_verify", CONTROL_DK_VERIFY, FALSE},
-#endif
- { US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE},
- { US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE},
- { US"enforce_sync", CONTROL_ENFORCE_SYNC, FALSE},
- { US"freeze", CONTROL_FREEZE, FALSE},
- { US"no_enforce_sync", CONTROL_NO_ENFORCE_SYNC, FALSE},
- { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE},
- { US"queue_only", CONTROL_QUEUE_ONLY, FALSE},
+ { US"dk_verify", CONTROL_DK_VERIFY, FALSE },
+#endif
+ { US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE },
+ { US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE },
+ { US"enforce_sync", CONTROL_ENFORCE_SYNC, FALSE },
+ { US"freeze", CONTROL_FREEZE, TRUE },
+ { US"no_callout_flush", CONTROL_NO_CALLOUT_FLUSH, FALSE },
+ { US"no_delay_flush", CONTROL_NO_DELAY_FLUSH, FALSE },
+ { US"no_enforce_sync", CONTROL_NO_ENFORCE_SYNC, FALSE },
+ { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE },
+ { US"no_pipelining", CONTROL_NO_PIPELINING, FALSE },
+ { US"queue_only", CONTROL_QUEUE_ONLY, FALSE },
#ifdef WITH_CONTENT_SCAN
- { US"no_mbox_unspool", CONTROL_NO_MBOX_UNSPOOL, FALSE},
+ { US"no_mbox_unspool", CONTROL_NO_MBOX_UNSPOOL, FALSE },
#endif
- { US"fakedefer", CONTROL_FAKEDEFER, TRUE},
- { US"fakereject", CONTROL_FAKEREJECT, TRUE},
- { US"submission", CONTROL_SUBMISSION, TRUE}
+ { US"fakedefer", CONTROL_FAKEDEFER, TRUE },
+ { US"fakereject", CONTROL_FAKEREJECT, TRUE },
+ { US"submission", CONTROL_SUBMISSION, TRUE },
+ { US"suppress_local_fixups", CONTROL_SUPPRESS_LOCAL_FIXUPS, FALSE }
};
/* Support data structures for Client SMTP Authorization. acl_verify_csa()
can be started by a name, or by a macro definition. */
s = readconf_readname(name, sizeof(name), s);
- if (*s == ':' || isupper(name[0] && *s == '=')) return yield;
+ if (*s == ':' || (isupper(name[0]) && *s == '=')) return yield;
/* If a verb is unrecognized, it may be another condition or modifier that
continues the previous verb. */
{
if (this == NULL)
{
- *error = string_sprintf("unknown ACL verb in \"%s\"", saveline);
+ *error = string_sprintf("unknown ACL verb \"%s\" in \"%s\"", name,
+ saveline);
return NULL;
}
}
/* The "set" modifier is different in that its argument is "name=value"
rather than just a value, and we can check the validity of the name, which
- gives us a variable number to insert into the data block. */
+ gives us a variable name to insert into the data block. The original ACL
+ variable names were acl_c0 ... acl_c9 and acl_m0 ... acl_m9. This was
+ extended to 20 of each type, but after that people successfully argued for
+ arbitrary names. In the new scheme, the names must start with acl_c or acl_m.
+ After that, we allow alphanumerics and underscores, but the first character
+ after c or m must be a digit or an underscore. This retains backwards
+ compatibility. */
if (c == ACLC_SET)
{
- if (Ustrncmp(s, "acl_", 4) != 0 || (s[4] != 'c' && s[4] != 'm') ||
- !isdigit(s[5]) || (!isspace(s[6]) && s[6] != '='))
+ uschar *endptr;
+
+ if (Ustrncmp(s, "acl_c", 5) != 0 &&
+ Ustrncmp(s, "acl_m", 5) != 0)
+ {
+ *error = string_sprintf("invalid variable name after \"set\" in ACL "
+ "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s);
+ return NULL;
+ }
+
+ endptr = s + 5;
+ if (!isdigit(*endptr) && *endptr != '_')
{
- *error = string_sprintf("unrecognized name after \"set\" in ACL "
- "modifier \"set %s\"", s);
+ *error = string_sprintf("invalid variable name after \"set\" in ACL "
+ "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)",
+ s);
return NULL;
}
- cond->u.varnumber = s[5] - '0';
- if (s[4] == 'm') cond->u.varnumber += ACL_C_MAX;
- s += 6;
+ while (*endptr != 0 && *endptr != '=' && !isspace(*endptr))
+ {
+ if (!isalnum(*endptr) && *endptr != '_')
+ {
+ *error = string_sprintf("invalid character \"%c\" in variable name "
+ "in ACL modifier \"set %s\"", *endptr, s);
+ return NULL;
+ }
+ endptr++;
+ }
+
+ cond->u.varname = string_copyn(s + 4, endptr - s - 4);
+ s = endptr;
while (isspace(*s)) s++;
}
+/*************************************************
+* Set up added header line(s) *
+*************************************************/
+
+/* This function is called by the add_header modifier, and also from acl_warn()
+to implement the now-deprecated way of adding header lines using "message" on a
+"warn" verb. The argument is treated as a sequence of header lines which are
+added to a chain, provided there isn't an identical one already there.
+
+Argument: string of header lines
+Returns: nothing
+*/
+
+static void
+setup_header(uschar *hstring)
+{
+uschar *p, *q;
+int hlen = Ustrlen(hstring);
+
+/* An empty string does nothing; otherwise add a final newline if necessary. */
+
+if (hlen <= 0) return;
+if (hstring[hlen-1] != '\n') hstring = string_sprintf("%s\n", hstring);
+
+/* Loop for multiple header lines, taking care about continuations */
+
+for (p = q = hstring; *p != 0; )
+ {
+ uschar *s;
+ int newtype = htype_add_bot;
+ header_line **hptr = &acl_added_headers;
+
+ /* Find next header line within the string */
+
+ for (;;)
+ {
+ q = Ustrchr(q, '\n');
+ if (*(++q) != ' ' && *q != '\t') break;
+ }
+
+ /* If the line starts with a colon, interpret the instruction for where to
+ add it. This temporarily sets up a new type. */
+
+ if (*p == ':')
+ {
+ if (strncmpic(p, US":after_received:", 16) == 0)
+ {
+ newtype = htype_add_rec;
+ p += 16;
+ }
+ else if (strncmpic(p, US":at_start_rfc:", 14) == 0)
+ {
+ newtype = htype_add_rfc;
+ p += 14;
+ }
+ else if (strncmpic(p, US":at_start:", 10) == 0)
+ {
+ newtype = htype_add_top;
+ p += 10;
+ }
+ else if (strncmpic(p, US":at_end:", 8) == 0)
+ {
+ newtype = htype_add_bot;
+ p += 8;
+ }
+ while (*p == ' ' || *p == '\t') p++;
+ }
+
+ /* See if this line starts with a header name, and if not, add X-ACL-Warn:
+ to the front of it. */
+
+ for (s = p; s < q - 1; s++)
+ {
+ if (*s == ':' || !isgraph(*s)) break;
+ }
+
+ s = string_sprintf("%s%.*s", (*s == ':')? "" : "X-ACL-Warn: ", q - p, p);
+ hlen = Ustrlen(s);
+
+ /* See if this line has already been added */
+
+ while (*hptr != NULL)
+ {
+ if (Ustrncmp((*hptr)->text, s, hlen) == 0) break;
+ hptr = &((*hptr)->next);
+ }
+
+ /* Add if not previously present */
+
+ if (*hptr == NULL)
+ {
+ header_line *h = store_get(sizeof(header_line));
+ h->text = s;
+ h->next = NULL;
+ h->type = newtype;
+ h->slen = hlen;
+ *hptr = h;
+ hptr = &(h->next);
+ }
+
+ /* Advance for next header line within the string */
+
+ p = q;
+ }
+}
+
+
+
+
/*************************************************
* Handle warnings *
*************************************************/
the message's headers, and/or writes information to the log. In each case, this
only happens once (per message for headers, per connection for log).
+** NOTE: The header adding action using the "message" setting is historic, and
+its use is now deprecated. The new add_header modifier should be used instead.
+
Arguments:
where ACL_WHERE_xxxx indicating which ACL this is
user_message message for adding to headers
static void
acl_warn(int where, uschar *user_message, uschar *log_message)
{
-int hlen;
-
if (log_message != NULL && log_message != user_message)
{
uschar *text;
return;
}
-/* Treat the user message as a sequence of one or more header lines. */
-
-hlen = Ustrlen(user_message);
-if (hlen > 0)
- {
- uschar *text, *p, *q;
-
- /* Add a final newline if not present */
-
- text = ((user_message)[hlen-1] == '\n')? user_message :
- string_sprintf("%s\n", user_message);
-
- /* Loop for multiple header lines, taking care about continuations */
-
- for (p = q = text; *p != 0; )
- {
- uschar *s;
- int newtype = htype_add_bot;
- header_line **hptr = &acl_warn_headers;
-
- /* Find next header line within the string */
-
- for (;;)
- {
- q = Ustrchr(q, '\n');
- if (*(++q) != ' ' && *q != '\t') break;
- }
-
- /* If the line starts with a colon, interpret the instruction for where to
- add it. This temporarily sets up a new type. */
-
- if (*p == ':')
- {
- if (strncmpic(p, US":after_received:", 16) == 0)
- {
- newtype = htype_add_rec;
- p += 16;
- }
- else if (strncmpic(p, US":at_start_rfc:", 14) == 0)
- {
- newtype = htype_add_rfc;
- p += 14;
- }
- else if (strncmpic(p, US":at_start:", 10) == 0)
- {
- newtype = htype_add_top;
- p += 10;
- }
- else if (strncmpic(p, US":at_end:", 8) == 0)
- {
- newtype = htype_add_bot;
- p += 8;
- }
- while (*p == ' ' || *p == '\t') p++;
- }
-
- /* See if this line starts with a header name, and if not, add X-ACL-Warn:
- to the front of it. */
-
- for (s = p; s < q - 1; s++)
- {
- if (*s == ':' || !isgraph(*s)) break;
- }
-
- s = string_sprintf("%s%.*s", (*s == ':')? "" : "X-ACL-Warn: ", q - p, p);
- hlen = Ustrlen(s);
-
- /* See if this line has already been added */
-
- while (*hptr != NULL)
- {
- if (Ustrncmp((*hptr)->text, s, hlen) == 0) break;
- hptr = &((*hptr)->next);
- }
-
- /* Add if not previously present */
-
- if (*hptr == NULL)
- {
- header_line *h = store_get(sizeof(header_line));
- h->text = s;
- h->next = NULL;
- h->type = newtype;
- h->slen = hlen;
- *hptr = h;
- hptr = &(h->next);
- }
-
- /* Advance for next header line within the string */
+/* The code for setting up header lines is now abstracted into a separate
+function so that it can be used for the add_header modifier as well. */
- p = q;
- }
- }
+setup_header(user_message);
}
address literals, but it's probably the most friendly thing to do. This is an
extension to CSA, so we allow it to be turned off for proper conformance. */
-if (string_is_ip_address(domain, NULL))
+if (string_is_ip_address(domain, NULL) != 0)
{
if (!dns_csa_use_reverse) return CSA_UNKNOWN;
dns_build_reverse(domain, target);
if (strcmpic(ss, US"helo") == 0)
{
if (slash != NULL) goto NO_OPTIONS;
- if (helo_verified) return OK;
- if (helo_verify_failed) return FAIL;
- if (smtp_verify_helo()) return helo_verified? OK : FAIL;
- return DEFER;
+ if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
+ return helo_verified? OK : FAIL;
}
/* Do Client SMTP Authorization checks in a separate function, and turn the
if (strcmpic(ss, US"header_syntax") == 0)
{
if (slash != NULL) goto NO_OPTIONS;
- if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP)
- {
- *log_msgptr = string_sprintf("cannot check header contents in ACL for %s "
- "(only possible in ACL for DATA)", acl_wherenames[where]);
- return ERROR;
- }
+ if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) goto WRONG_ACL;
rc = verify_check_headers(log_msgptr);
if (rc != OK && smtp_return_error_details && *log_msgptr != NULL)
*user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
return rc;
}
+/* Check that no recipient of this message is "blind", that is, every envelope
+recipient must be mentioned in either To: or Cc:. */
+
+if (strcmpic(ss, US"not_blind") == 0)
+ {
+ if (slash != NULL) goto NO_OPTIONS;
+ if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) goto WRONG_ACL;
+ rc = verify_check_notblind();
+ if (rc != OK)
+ {
+ *log_msgptr = string_sprintf("bcc recipient detected");
+ if (smtp_return_error_details)
+ *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
+ }
+ return rc;
+ }
/* The remaining verification tests check recipient and sender addresses,
either from the envelope or from the header. There are a number of
if (strcmpic(ss, US"header_sender") == 0)
{
- if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP)
- {
- *log_msgptr = string_sprintf("cannot check header contents in ACL for %s "
- "(only possible in ACL for DATA)", acl_wherenames[where]);
- return ERROR;
- }
+ if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) goto WRONG_ACL;
verify_header_sender = TRUE;
}
callout_overall, callout_connect, se_mailfrom, pm_mailfrom, NULL);
HDEBUG(D_acl) debug_printf("----------- end verify ------------\n");
+ *basic_errno = addr2.basic_errno;
*log_msgptr = addr2.message;
*user_msgptr = (addr2.user_message != NULL)?
addr2.user_message : addr2.message;
- *basic_errno = addr2.basic_errno;
+
+ /* Allow details for temporary error if the address is so flagged. */
+ if (testflag((&addr2), af_pass_message)) acl_temp_details = TRUE;
/* Make $address_data visible */
deliver_address_data = addr2.p.address_data;
*log_msgptr = string_sprintf("unexpected '/' found in \"%s\" "
"(this verify item has no options)", arg);
return ERROR;
+
+/* Calls in the wrong ACL come here */
+
+WRONG_ACL:
+*log_msgptr = string_sprintf("cannot check header contents in ACL for %s "
+ "(only possible in ACL for DATA)", acl_wherenames[where]);
+return ERROR;
}
Arguments:
arg the option string for ratelimit=
+ where ACL_WHERE_xxxx indicating which ACL this is
log_msgptr for error messages
Returns: OK - Sender's rate is above limit
*/
static int
-acl_ratelimit(uschar *arg, uschar **log_msgptr)
+acl_ratelimit(uschar *arg, int where, uschar **log_msgptr)
{
double limit, period;
uschar *ss, *key;
+ (double)tv.tv_usec / 1000000.0;
double prev_time = (double)dbd->time_stamp
+ (double)dbd->time_usec / 1000000.0;
- double interval = this_time - prev_time;
-
- double i_over_p = interval / period;
- double a = exp(-i_over_p);
/* We must avoid division by zero, and deal gracefully with the clock going
backwards. If we blunder ahead when time is in reverse then the computed
- rate will become bogusly huge. Clamp i/p to a very small number instead. */
+ rate will be bogus. To be safe we clamp interval to a very small number. */
+
+ double interval = this_time - prev_time <= 0.0 ? 1e-9
+ : this_time - prev_time;
- if (i_over_p <= 0.0) i_over_p = 1e-9;
+ double i_over_p = interval / period;
+ double a = exp(-i_over_p);
dbd->time_stamp = tv.tv_sec;
dbd->time_usec = tv.tv_usec;
if (per_byte)
dbd->rate = (message_size < 0 ? 0.0 : (double)message_size)
* (1 - a) / i_over_p + a * dbd->rate;
+ else if (per_cmd && where == ACL_WHERE_NOTSMTP)
+ dbd->rate = (double)recipients_count
+ * (1 - a) / i_over_p + a * dbd->rate;
else
dbd->rate = (1 - a) / i_over_p + a * dbd->rate;
}
{
uschar *user_message = NULL;
uschar *log_message = NULL;
-uschar *p;
+uschar *p = NULL;
int rc = OK;
#ifdef WITH_CONTENT_SCAN
int sep = '/';
if (cb->type == ACLC_SET)
{
- int n = cb->u.varnumber;
- int t = (n < ACL_C_MAX)? 'c' : 'm';
- if (n >= ACL_C_MAX) n -= ACL_C_MAX;
- debug_printf("acl_%c%d ", t, n);
- lhswidth += 7;
+ debug_printf("acl_%s ", cb->u.varname);
+ lhswidth += 5 + Ustrlen(cb->u.varname);
}
debug_printf("= %s\n", cb->arg);
switch(cb->type)
{
+ case ACLC_ADD_HEADER:
+ setup_header(arg);
+ break;
+
/* A nested ACL that returns "discard" makes sense only for an "accept" or
"discard" verb. */
TRUE, NULL);
break;
-#ifdef EXPERIMENTAL_BRIGHTMAIL
+ #ifdef EXPERIMENTAL_BRIGHTMAIL
case ACLC_BMI_OPTIN:
{
int old_pool = store_pool;
store_pool = old_pool;
}
break;
-#endif
+ #endif
case ACLC_CONDITION:
if (Ustrspn(arg, "0123456789") == Ustrlen(arg)) /* Digits, or empty */
*log_msgptr = string_sprintf("invalid \"condition\" value \"%s\"", arg);
break;
+ case ACLC_CONTINUE: /* Always succeeds */
+ break;
+
case ACLC_CONTROL:
control_type = decode_control(arg, &p, where, log_msgptr);
switch(control_type)
{
-#ifdef EXPERIMENTAL_BRIGHTMAIL
+ case CONTROL_AUTH_UNADVERTISED:
+ allow_auth_unadvertised = TRUE;
+ break;
+
+ #ifdef EXPERIMENTAL_BRIGHTMAIL
case CONTROL_BMI_RUN:
bmi_run = 1;
break;
-#endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
+ #endif
+
+ #ifdef EXPERIMENTAL_DOMAINKEYS
case CONTROL_DK_VERIFY:
dk_do_verify = 1;
break;
-#endif
+ #endif
+
case CONTROL_ERROR:
return ERROR;
smtp_enforce_sync = FALSE;
break;
-#ifdef WITH_CONTENT_SCAN
+ #ifdef WITH_CONTENT_SCAN
case CONTROL_NO_MBOX_UNSPOOL:
no_mbox_unspool = TRUE;
break;
-#endif
+ #endif
case CONTROL_NO_MULTILINE:
no_multiline_responses = TRUE;
break;
+ case CONTROL_NO_PIPELINING:
+ pipelining_enable = FALSE;
+ break;
+
+ case CONTROL_NO_DELAY_FLUSH:
+ disable_delay_flush = TRUE;
+ break;
+
+ case CONTROL_NO_CALLOUT_FLUSH:
+ disable_callout_flush = TRUE;
+ break;
+
case CONTROL_FAKEDEFER:
case CONTROL_FAKEREJECT:
fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL;
case CONTROL_FREEZE:
deliver_freeze = TRUE;
deliver_frozen_at = time(NULL);
+ freeze_tell = freeze_tell_config; /* Reset to configured value */
+ if (Ustrncmp(p, "/no_tell", 8) == 0)
+ {
+ p += 8;
+ freeze_tell = NULL;
+ }
+ if (*p != 0)
+ {
+ *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
+ return ERROR;
+ }
break;
case CONTROL_QUEUE_ONLY:
submission_domain = string_copyn(p+8, pp-p-8);
p = pp;
}
+ /* The name= option must be last, because it swallows the rest of
+ the string. */
else if (Ustrncmp(p, "/name=", 6) == 0)
{
uschar *pp = p + 6;
- while (*pp != 0 && *pp != '/') pp++;
- originator_name = string_copy(parse_fix_phrase(p+6, pp-p-6,
+ while (*pp != 0) pp++;
+ submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6,
big_buffer, big_buffer_size));
p = pp;
}
return ERROR;
}
break;
+
+ case CONTROL_SUPPRESS_LOCAL_FIXUPS:
+ suppress_local_fixups = TRUE;
+ break;
}
break;
-#ifdef WITH_CONTENT_SCAN
+ #ifdef WITH_CONTENT_SCAN
case ACLC_DECODE:
rc = mime_decode(&arg);
break;
-#endif
+ #endif
case ACLC_DELAY:
{
can't. The poll() function does not do the right thing, and in any case
it is not always available.
- NOTE: If ever this state of affairs changes, remember that we may be
+ NOTE 1: If ever this state of affairs changes, remember that we may be
dealing with stdin/stdout here, in addition to TCP/IP connections.
- Whatever is done must work in both cases. To detected the stdin/stdout
- case, check for smtp_in or smtp_out being NULL. */
+ Also, delays may be specified for non-SMTP input, where smtp_out and
+ smtp_in will be NULL. Whatever is done must work in all cases.
+
+ NOTE 2: The added feature of flushing the output before a delay must
+ apply only to SMTP input. Hence the test for smtp_out being non-NULL.
+ */
else
{
+ if (smtp_out != NULL && !disable_delay_flush) mac_smtp_fflush();
while (delay > 0) delay = sleep(delay);
}
}
}
break;
-#ifdef WITH_OLD_DEMIME
+ #ifdef WITH_OLD_DEMIME
case ACLC_DEMIME:
rc = demime(&arg);
break;
-#endif
+ #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
- case ACLC_DK_DOMAIN_SOURCE:
+ #ifdef EXPERIMENTAL_DOMAINKEYS
+ case ACLC_DK_DOMAIN_SOURCE:
if (dk_verify_block == NULL) { rc = FAIL; break; };
/* check header source of domain against given string */
switch (dk_verify_block->address_source) {
rc = match_isinlist(US"none", &arg, 0, NULL,
NULL, MCL_STRING, TRUE, NULL);
break;
- }
- break;
- case ACLC_DK_POLICY:
+ }
+ break;
+
+ case ACLC_DK_POLICY:
if (dk_verify_block == NULL) { rc = FAIL; break; };
/* check policy against given string, default FAIL */
rc = FAIL;
if (dk_verify_block->testing)
rc = match_isinlist(US"testing", &arg, 0, NULL,
NULL, MCL_STRING, TRUE, NULL);
- break;
- case ACLC_DK_SENDER_DOMAINS:
+ break;
+
+ case ACLC_DK_SENDER_DOMAINS:
if (dk_verify_block == NULL) { rc = FAIL; break; };
if (dk_verify_block->domain != NULL)
rc = match_isinlist(dk_verify_block->domain, &arg, 0, &domainlist_anchor,
NULL, MCL_DOMAIN, TRUE, NULL);
else rc = FAIL;
- break;
- case ACLC_DK_SENDER_LOCAL_PARTS:
+ break;
+
+ case ACLC_DK_SENDER_LOCAL_PARTS:
if (dk_verify_block == NULL) { rc = FAIL; break; };
if (dk_verify_block->local_part != NULL)
rc = match_isinlist(dk_verify_block->local_part, &arg, 0, &localpartlist_anchor,
NULL, MCL_LOCALPART, TRUE, NULL);
else rc = FAIL;
- break;
- case ACLC_DK_SENDERS:
+ break;
+
+ case ACLC_DK_SENDERS:
if (dk_verify_block == NULL) { rc = FAIL; break; };
if (dk_verify_block->address != NULL)
rc = match_address_list(dk_verify_block->address, TRUE, TRUE, &arg, NULL, -1, 0, NULL);
else rc = FAIL;
- break;
- case ACLC_DK_STATUS:
+ break;
+
+ case ACLC_DK_STATUS:
if (dk_verify_block == NULL) { rc = FAIL; break; };
if (dk_verify_block->result > 0) {
switch(dk_verify_block->result) {
rc = match_isinlist(US"bad", &arg, 0, NULL,
NULL, MCL_STRING, TRUE, NULL);
break;
+ }
}
- }
- break;
-#endif
+ break;
+ #endif
case ACLC_DNSLISTS:
rc = verify_check_dnsbl(&arg);
&deliver_localpart_data);
break;
+ case ACLC_LOG_REJECT_TARGET:
+ {
+ int logbits = 0;
+ int sep = 0;
+ uschar *s = arg;
+ uschar *ss;
+ while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))
+ != NULL)
+ {
+ if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN;
+ else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC;
+ else if (Ustrcmp(ss, "reject") == 0) logbits |= LOG_REJECT;
+ else
+ {
+ logbits |= LOG_MAIN|LOG_REJECT;
+ log_write(0, LOG_MAIN|LOG_PANIC, "unknown log name \"%s\" in "
+ "\"log_reject_target\" in %s ACL", ss, acl_wherenames[where]);
+ }
+ }
+ log_reject_target = logbits;
+ }
+ break;
+
case ACLC_LOGWRITE:
{
int logbits = 0;
s++;
}
while (isspace(*s)) s++;
+
+
if (logbits == 0) logbits = LOG_MAIN;
log_write(0, logbits, "%s", string_printing(s));
}
break;
-#ifdef WITH_CONTENT_SCAN
+ #ifdef WITH_CONTENT_SCAN
case ACLC_MALWARE:
{
- /* Seperate the regular expression and any optional parameters. */
+ /* Separate the regular expression and any optional parameters. */
uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size);
/* Run the malware backend. */
rc = malware(&ss);
break;
case ACLC_MIME_REGEX:
- rc = mime_regex(&arg);
+ rc = mime_regex(&arg);
break;
-#endif
+ #endif
case ACLC_RATELIMIT:
- rc = acl_ratelimit(arg, log_msgptr);
+ rc = acl_ratelimit(arg, where, log_msgptr);
break;
case ACLC_RECIPIENTS:
&recipient_data);
break;
-#ifdef WITH_CONTENT_SCAN
- case ACLC_REGEX:
- rc = regex(&arg);
+ #ifdef WITH_CONTENT_SCAN
+ case ACLC_REGEX:
+ rc = regex(&arg);
break;
-#endif
+ #endif
case ACLC_SENDER_DOMAINS:
{
case ACLC_SET:
{
int old_pool = store_pool;
- if (cb->u.varnumber < ACL_C_MAX) store_pool = POOL_PERM;
- acl_var[cb->u.varnumber] = string_copy(arg);
+ if (cb->u.varname[0] == 'c') store_pool = POOL_PERM;
+ acl_var_create(cb->u.varname)->data.ptr = string_copy(arg);
store_pool = old_pool;
}
break;
-#ifdef WITH_CONTENT_SCAN
+ #ifdef WITH_CONTENT_SCAN
case ACLC_SPAM:
{
/* Seperate the regular expression and any optional parameters. */
}
}
break;
-#endif
+ #endif
-#ifdef EXPERIMENTAL_SPF
+ #ifdef EXPERIMENTAL_SPF
case ACLC_SPF:
rc = spf_process(&arg, sender_address);
break;
-#endif
+ #endif
/* If the verb is WARN, discard any user message from verification, because
such messages are SMTP responses, not header additions. The latter come
/* If the result is the one for which "message" and/or "log_message" are used,
-handle the values of these options. Most verbs have but a single return for
-which the messages are relevant, but for "discard", it's useful to have the log
-message both when it succeeds and when it fails. Also, for an "accept" that
-appears in a QUIT ACL, we want to handle the user message. Since only "accept"
-and "warn" are permitted in that ACL, we don't need to test the verb.
-
-These modifiers act in different ways:
+handle the values of these modifiers. If there isn't a log message set, we make
+it the same as the user message.
"message" is a user message that will be included in an SMTP response. Unless
it is empty, it overrides any previously set user message.
"log_message" is a non-user message, and it adds to any existing non-user
message that is already set.
-If there isn't a log message set, we make it the same as the user message. */
+Most verbs have but a single return for which the messages are relevant, but
+for "discard", it's useful to have the log message both when it succeeds and
+when it fails. For "accept", the message is used in the OK case if there is no
+"endpass", but (for backwards compatibility) in the FAIL case if "endpass" is
+present. */
-if (((rc == FAIL_DROP)? FAIL : rc) == msgcond[verb] ||
- (verb == ACL_DISCARD && rc == OK) ||
- (where == ACL_WHERE_QUIT))
+if (*epp && rc == OK) user_message = NULL;
+
+if (((1<<rc) & msgcond[verb]) != 0)
{
uschar *expmessage;
+ uschar *old_user_msgptr = *user_msgptr;
+ uschar *old_log_msgptr = (*log_msgptr != NULL)? *log_msgptr : old_user_msgptr;
/* If the verb is "warn", messages generated by conditions (verification or
- nested ACLs) are discarded. Only messages specified at this level are used.
+ nested ACLs) are always discarded. This also happens for acceptance verbs
+ when they actually do accept. Only messages specified at this level are used.
However, the value of an existing message is available in $acl_verify_message
during expansions. */
- uschar *old_user_msgptr = *user_msgptr;
- uschar *old_log_msgptr = (*log_msgptr != NULL)? *log_msgptr : old_user_msgptr;
-
- if (verb == ACL_WARN) *log_msgptr = *user_msgptr = NULL;
+ if (verb == ACL_WARN ||
+ (rc == OK && (verb == ACL_ACCEPT || verb == ACL_DISCARD)))
+ *log_msgptr = *user_msgptr = NULL;
if (user_message != NULL)
{
case ACL_WARN:
if (cond == OK)
acl_warn(where, *user_msgptr, *log_msgptr);
- else if (cond == DEFER)
+ else if (cond == DEFER && (log_extra_selector & LX_acl_warn_skipped) != 0)
log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: "
"condition test deferred%s%s", host_and_ident(TRUE),
(*log_msgptr == NULL)? US"" : US": ",
*user_msgptr = *log_msgptr = NULL;
sender_verified_failed = NULL;
ratelimiters_cmd = NULL;
+log_reject_target = LOG_MAIN|LOG_REJECT;
if (where == ACL_WHERE_RCPT)
{
return ERROR;
}
-/* Before giving an error response, take a look at the length of any user
-message, and split it up into multiple lines if possible. */
+/* Before giving a response, take a look at the length of any user message, and
+split it up into multiple lines if possible. */
-if (rc != OK && *user_msgptr != NULL && Ustrlen(*user_msgptr) > 75)
- {
- uschar *s = *user_msgptr = string_copy(*user_msgptr);
- uschar *ss = s;
+*user_msgptr = string_split_message(*user_msgptr);
+if (fake_response != OK)
+ fake_response_text = string_split_message(fake_response_text);
- for (;;)
- {
- int i = 0;
- while (i < 75 && *ss != 0 && *ss != '\n') ss++, i++;
- if (*ss == 0) break;
- if (*ss == '\n')
- s = ++ss;
- else
- {
- uschar *t = ss + 1;
- uschar *tt = NULL;
- while (--t > s + 35)
- {
- if (*t == ' ')
- {
- if (t[-1] == ':') { tt = t; break; }
- if (tt == NULL) tt = t;
- }
- }
+return rc;
+}
- if (tt == NULL) /* Can't split behind - try ahead */
- {
- t = ss + 1;
- while (*t != 0)
- {
- if (*t == ' ' || *t == '\n')
- { tt = t; break; }
- t++;
- }
- }
- if (tt == NULL) break; /* Can't find anywhere to split */
- *tt = '\n';
- s = ss = tt+1;
- }
- }
+
+/*************************************************
+* Create ACL variable *
+*************************************************/
+
+/* Create an ACL variable or reuse an existing one. ACL variables are in a
+binary tree (see tree.c) with acl_var_c and acl_var_m as root nodes.
+
+Argument:
+ name pointer to the variable's name, starting with c or m
+
+Returns the pointer to variable's tree node
+*/
+
+tree_node *
+acl_var_create(uschar *name)
+{
+tree_node *node, **root;
+root = (name[0] == 'c')? &acl_var_c : &acl_var_m;
+node = tree_search(*root, name);
+if (node == NULL)
+ {
+ node = store_get(sizeof(tree_node) + Ustrlen(name));
+ Ustrcpy(node->name, name);
+ (void)tree_insertnode(root, node);
}
+node->data.ptr = NULL;
+return node;
+}
-return rc;
+
+
+/*************************************************
+* Write an ACL variable in spool format *
+*************************************************/
+
+/* This function is used as a callback for tree_walk when writing variables to
+the spool file. To retain spool file compatibility, what is written is -aclc or
+-aclm followed by the rest of the name and the data length, space separated,
+then the value itself, starting on a new line, and terminated by an additional
+newline. When we had only numbered ACL variables, the first line might look
+like this: "-aclc 5 20". Now it might be "-aclc foo 20" for the variable called
+acl_cfoo.
+
+Arguments:
+ name of the variable
+ value of the variable
+ ctx FILE pointer (as a void pointer)
+
+Returns: nothing
+*/
+
+void
+acl_var_write(uschar *name, uschar *value, void *ctx)
+{
+FILE *f = (FILE *)ctx;
+fprintf(f, "-acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value);
}
/* End of acl.c */