#ifdef WITH_CONTENT_SCAN
ACLC_REGEX,
#endif
+ ACLC_REMOVE_HEADER,
ACLC_SENDER_DOMAINS,
ACLC_SENDERS,
ACLC_SET,
#ifdef WITH_CONTENT_SCAN
US"regex",
#endif
+ US"remove_header",
US"sender_domains", US"senders", US"set",
#ifdef WITH_CONTENT_SCAN
US"spam",
checking functions. */
static uschar cond_expand_at_top[] = {
- TRUE, /* acl */
+ FALSE, /* acl */
TRUE, /* add_header */
FALSE, /* authenticated */
#ifdef EXPERIMENTAL_BRIGHTMAIL
#ifdef WITH_CONTENT_SCAN
TRUE, /* regex */
#endif
+ TRUE, /* remove_header */
FALSE, /* sender_domains */
FALSE, /* senders */
TRUE, /* set */
#ifdef WITH_CONTENT_SCAN
FALSE, /* regex */
#endif
+ TRUE, /* remove_header */
FALSE, /* sender_domains */
FALSE, /* senders */
TRUE, /* set */
(1<<ACL_WHERE_MIME)),
#endif
+ (unsigned int)
+ ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* remove_header */
+ (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ (1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
+ (1<<ACL_WHERE_NOTSMTP_START)),
+
(1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)| /* sender_domains */
(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
/* Enable recursion between acl_check_internal() and acl_check_condition() */
-static int acl_check_internal(int, address_item *, uschar *, int, uschar **,
- uschar **);
+static int acl_check_wargs(int, address_item *, uschar *, int, uschar **,
+ uschar **);
/*************************************************
uschar *p, *q;
int hlen = Ustrlen(hstring);
-/* An empty string does nothing; otherwise add a final newline if necessary. */
+/* Ignore any leading newlines */
+while (*hstring == '\n') hstring++, hlen--;
+/* An empty string does nothing; ensure exactly one final newline. */
if (hlen <= 0) return;
-if (hstring[hlen-1] != '\n') hstring = string_sprintf("%s\n", hstring);
+if (hstring[--hlen] != '\n') hstring = string_sprintf("%s\n", hstring);
+else while(hstring[--hlen] == '\n') hstring[hlen+1] = '\0';
/* Loop for multiple header lines, taking care about continuations */
+/*************************************************
+* List the added header lines *
+*************************************************/
+uschar *
+fn_hdrs_added(void)
+{
+uschar * ret = NULL;
+header_line * h = acl_added_headers;
+uschar * s;
+uschar * cp;
+int size = 0;
+int ptr = 0;
+
+if (!h) return NULL;
+
+do
+ {
+ s = h->text;
+ while ((cp = Ustrchr(s, '\n')) != NULL)
+ {
+ if (cp[1] == '\0') break;
+
+ /* contains embedded newline; needs doubling */
+ ret = string_cat(ret, &size, &ptr, s, cp-s+1);
+ ret = string_cat(ret, &size, &ptr, US"\n", 1);
+ s = cp+1;
+ }
+ /* last bit of header */
+
+ ret = string_cat(ret, &size, &ptr, s, cp-s+1); /* newline-sep list */
+ }
+while((h = h->next));
+
+ret[ptr-1] = '\0'; /* overwrite last newline */
+return ret;
+}
+
+
+/*************************************************
+* Set up removed header line(s) *
+*************************************************/
+
+/* This function is called by the remove_header modifier. The argument is
+treated as a sequence of header names which are added to a colon separated
+list, provided there isn't an identical one already there.
+
+Argument: string of header names
+Returns: nothing
+*/
+
+static void
+setup_remove_header(uschar *hnames)
+{
+if (*hnames != 0)
+ {
+ if (acl_removed_headers == NULL)
+ acl_removed_headers = hnames;
+ else
+ acl_removed_headers = string_sprintf("%s : %s", acl_removed_headers, hnames);
+ }
+}
+
+
/*************************************************
* Handle warnings *
"discard" verb. */
case ACLC_ACL:
- rc = acl_check_internal(where, addr, arg, level+1, user_msgptr, log_msgptr);
- if (rc == DISCARD && verb != ACL_ACCEPT && verb != ACL_DISCARD)
- {
- *log_msgptr = string_sprintf("nested ACL returned \"discard\" for "
- "\"%s\" command (only allowed with \"accept\" or \"discard\")",
- verbs[verb]);
- return ERROR;
- }
+ rc = acl_check_wargs(where, addr, arg, level+1, user_msgptr, log_msgptr);
+ if (rc == DISCARD && verb != ACL_ACCEPT && verb != ACL_DISCARD)
+ {
+ *log_msgptr = string_sprintf("nested ACL returned \"discard\" for "
+ "\"%s\" command (only allowed with \"accept\" or \"discard\")",
+ verbs[verb]);
+ return ERROR;
+ }
break;
case ACLC_AUTHENTICATED:
break;
#endif
+ case ACLC_REMOVE_HEADER:
+ setup_remove_header(arg);
+ break;
+
case ACLC_SENDER_DOMAINS:
{
uschar *sdomain;
/* Same args as acl_check_internal() above, but the string s is
the name of an ACL followed optionally by up to 9 space-separated arguments.
The name and args are separately expanded. Args go into $acl_arg globals. */
-int
-acl_check_args(int where, address_item *addr, uschar *s, int level,
+static int
+acl_check_wargs(int where, address_item *addr, uschar *s, int level,
uschar **user_msgptr, uschar **log_msgptr)
{
uschar * tmp;
+uschar * tmp_arg[9]; /* must match acl_arg[] */
+uschar * sav_arg[9]; /* must match acl_arg[] */
+int sav_narg;
uschar * name;
+int i;
+int ret;
if (!(tmp = string_dequote(&s)) || !(name = expand_string(tmp)))
goto bad;
-for (acl_narg = 0; acl_narg < sizeof(acl_arg)/sizeof(*acl_arg); acl_narg++)
+for (i = 0; i < 9; i++)
{
while (*s && isspace(*s)) s++;
if (!*s) break;
- if (!(tmp = string_dequote(&s)) || !(acl_arg[acl_narg] = expand_string(tmp)))
+ if (!(tmp = string_dequote(&s)) || !(tmp_arg[i] = expand_string(tmp)))
{
tmp = name;
goto bad;
}
}
-return acl_check_internal(where, addr, name, level+1, user_msgptr, log_msgptr);
+sav_narg = acl_narg;
+acl_narg = i;
+for (i = 0; i < acl_narg; i++)
+ {
+ sav_arg[i] = acl_arg[i];
+ acl_arg[i] = tmp_arg[i];
+ }
+while (i < 9)
+ {
+ sav_arg[i] = acl_arg[i];
+ acl_arg[i++] = NULL;
+ }
+
+ret = acl_check_internal(where, addr, name, level, user_msgptr, log_msgptr);
+
+acl_narg = sav_narg;
+for (i = 0; i < 9; i++) acl_arg[i] = sav_arg[i];
+return ret;
bad:
if (expand_string_forcedfail) return ERROR;
* Check access using an ACL *
*************************************************/
+/* Alternate interface for ACL, used by expansions */
+int
+acl_eval(int where, uschar *s, uschar **user_msgptr, uschar **log_msgptr)
+{
+address_item adb;
+address_item *addr = NULL;
+
+*user_msgptr = *log_msgptr = NULL;
+sender_verified_failed = NULL;
+ratelimiters_cmd = NULL;
+log_reject_target = LOG_MAIN|LOG_REJECT;
+
+if (where == ACL_WHERE_RCPT)
+ {
+ adb = address_defaults;
+ addr = &adb;
+ addr->address = expand_string(US"$local_part@$domain");
+ addr->domain = deliver_domain;
+ addr->local_part = deliver_localpart;
+ addr->cc_local_part = deliver_localpart;
+ addr->lc_local_part = deliver_localpart;
+ }
+
+return acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr);
+}
+
+
+
/* This is the external interface for ACL checks. It sets up an address and the
expansions for $domain and $local_part when called after RCPT, then calls
acl_check_internal() to do the actual work.
DEFER can't tell at the moment
ERROR disaster
*/
+int acl_where = ACL_WHERE_UNKNOWN;
int
acl_check(int where, uschar *recipient, uschar *s, uschar **user_msgptr,
deliver_localpart = addr->local_part;
}
+acl_where = where;
rc = acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr);
+acl_where = ACL_WHERE_UNKNOWN;
/* Cutthrough - if requested,
and WHERE_RCPT and not yet opened conn as result of recipient-verify,
}
-
/*************************************************
* Create ACL variable *
*************************************************/