*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* Code for handling Access Control Lists (ACLs) */
/* ACL conditions/modifiers: "delay", "control", "continue", "endpass",
"message", "log_message", "log_reject_target", "logwrite", "queue" and "set" are
modifiers that look like conditions but always return TRUE. They are used for
-their side effects. */
+their side effects. Do not invent new modifier names that result in one name
+being the prefix of another; the binary-search in the list will go wrong. */
typedef struct condition_def {
uschar *name;
CONTROL_NO_MULTILINE,
CONTROL_NO_PIPELINING,
- CONTROL_QUEUE_ONLY,
+ CONTROL_QUEUE,
CONTROL_SUBMISSION,
CONTROL_SUPPRESS_LOCAL_FIXUPS,
#ifdef SUPPORT_I18N
ACL_BIT_NOTSMTP | ACL_BIT_NOTSMTP_START
},
-[CONTROL_QUEUE_ONLY] =
- { US"queue_only", FALSE,
+[CONTROL_QUEUE] =
+ { US"queue", TRUE,
(unsigned)
~(ACL_BIT_MAIL | ACL_BIT_RCPT |
ACL_BIT_PREDATA | ACL_BIT_DATA |
ACL_BIT_NOTSMTP | ACL_BIT_MIME)
},
-
[CONTROL_SUBMISSION] =
{ US"submission", TRUE,
(unsigned)
*error = NULL;
-while ((s = (*func)()) != NULL)
+while ((s = (*func)()))
{
int v, c;
BOOL negated = FALSE;
/* Conditions (but not verbs) are allowed to be negated by an initial
exclamation mark. */
- while (isspace(*s)) s++;
- if (*s == '!')
+ if (Uskip_whitespace(&s) == '!')
{
negated = TRUE;
s++;
}
cond->u.varname = string_copyn(s, 18);
s = endptr;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
}
else
#endif
cond->u.varname = string_copyn(s + 4, endptr - s - 4);
s = endptr;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
}
/* For "set", we are now positioned for the data. For the others, only
conditions[c].is_modifier ? US"modifier" : US"condition");
return NULL;
}
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
cond->arg = string_copy(s);
}
}
{ US"not_blind", VERIFY_NOT_BLIND, ACL_BIT_DATA | ACL_BIT_NOTSMTP, FALSE, 0 },
{ US"header_sender", VERIFY_HDR_SNDR, ACL_BIT_DATA | ACL_BIT_NOTSMTP, FALSE, 0 },
{ US"sender", VERIFY_SNDR, ACL_BIT_MAIL | ACL_BIT_RCPT
- |ACL_BIT_PREDATA | ACL_BIT_DATA | ACL_BIT_NOTSMTP,
+ | ACL_BIT_PREDATA | ACL_BIT_DATA | ACL_BIT_NOTSMTP,
FALSE, 6 },
{ US"recipient", VERIFY_RCPT, ACL_BIT_RCPT, FALSE, 0 },
{ US"header_names_ascii", VERIFY_HDR_NAMES_ASCII, ACL_BIT_DATA | ACL_BIT_NOTSMTP, TRUE, 0 },
uschar *slash = Ustrchr(arg, '/');
const uschar *list = arg;
-uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
+uschar *ss = string_nextinlist(&list, &sep, NULL, 0);
verify_type_t * vp;
if (!ss) goto BAD_VERIFY;
in place of the actual sender (rare special-case requirement). */
{
uschar *s = ss + 6;
- if (*s == 0)
+ if (!*s)
verify_sender_address = sender_address;
else
{
else if (strncmpic(ss, US"callout", 7) == 0)
{
callout = CALLOUT_TIMEOUT_DEFAULT;
- ss += 7;
- if (*ss != 0)
+ if (*(ss += 7))
{
while (isspace(*ss)) ss++;
if (*ss++ == '=')
{
const uschar * sublist = ss;
int optsep = ',';
- uschar buffer[256];
- uschar * opt;
while (isspace(*sublist)) sublist++;
- while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer))))
+ for (uschar * opt; opt = string_nextinlist(&sublist, &optsep, NULL, 0); )
{
callout_opt_t * op;
double period = 1.0F;
}
sender_vaddr = verify_checked_sender(verify_sender_address);
- if (sender_vaddr != NULL && /* Previously checked */
- callout <= 0) /* No callout needed this time */
+ if ( sender_vaddr /* Previously checked */
+ && callout <= 0) /* No callout needed this time */
{
/* If the "routed" flag is set, it means that routing worked before, so
this check can give OK (the saved return code value, if set, belongs to a
*basic_errno = sender_vaddr->basic_errno;
else
DEBUG(D_acl)
- {
if (Ustrcmp(sender_vaddr->address, verify_sender_address) != 0)
debug_printf_indent("sender %s verified ok as %s\n",
verify_sender_address, sender_vaddr->address);
else
debug_printf_indent("sender %s verified ok\n",
verify_sender_address);
- }
}
else
rc = OK; /* Null sender */
*basic_errno = addr2.basic_errno;
*log_msgptr = addr2.message;
- *user_msgptr = (addr2.user_message != NULL)?
- addr2.user_message : addr2.message;
+ *user_msgptr = addr2.user_message ? addr2.user_message : addr2.message;
/* Allow details for temporary error if the address is so flagged. */
if (testflag((&addr2), af_pass_message)) f.acl_temp_details = TRUE;
/* We have a result from the relevant test. Handle defer overrides first. */
-if (rc == DEFER && (defer_ok ||
- (callout_defer_ok && *basic_errno == ERRNO_CALLOUTDEFER)))
+if ( rc == DEFER
+ && ( defer_ok
+ || callout_defer_ok && *basic_errno == ERRNO_CALLOUTDEFER
+ ) )
{
HDEBUG(D_acl) debug_printf_indent("verify defer overridden by %s\n",
defer_ok? "defer_ok" : "callout_defer_ok");
/* If we've failed a sender, set up a recipient message, and point
sender_verified_failed to the address item that actually failed. */
-if (rc != OK && verify_sender_address != NULL)
+if (rc != OK && verify_sender_address)
{
if (rc != DEFER)
*log_msgptr = *user_msgptr = US"Sender verify failed";
/* Verifying an address messes up the values of $domain and $local_part,
so reset them before returning if this is a RCPT ACL. */
-if (addr != NULL)
+if (addr)
{
deliver_domain = addr->domain;
deliver_localpart = addr->local_part;
* Check argument for control= modifier *
*************************************************/
-/* Called from acl_check_condition() below
+/* Called from acl_check_condition() below.
+To handle the case "queue_only" we accept an _ in the
+initial / option-switch position.
Arguments:
arg the argument string for control=
{
int idx, len;
control_def * d;
+uschar c;
if ( (idx = find_control(arg, controls_list, nelem(controls_list))) < 0
- || ( arg[len = Ustrlen((d = controls_list+idx)->name)] != 0
- && (!d->has_option || arg[len] != '/')
+ || ( (c = arg[len = Ustrlen((d = controls_list+idx)->name)]) != 0
+ && (!d->has_option || c != '/' && c != '_')
) )
{
*log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
/* Parse the other options. */
-while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)))
+while ((ss = string_nextinlist(&arg, &sep, NULL, 0)))
{
if (strcmpic(ss, US"leaky") == 0) leaky = TRUE;
else if (strcmpic(ss, US"strict") == 0) strict = TRUE;
cancel_cutthrough_connection(TRUE, US"item frozen");
break;
- case CONTROL_QUEUE_ONLY:
+ case CONTROL_QUEUE:
f.queue_only_policy = TRUE;
+ if (Ustrcmp(p, "_only") == 0)
+ p += 5;
+ else while (*p == '/')
+ if (Ustrncmp(p, "/only", 5) == 0)
+ { p += 5; f.queue_smtp = FALSE; }
+ else if (Ustrncmp(p, "/first_pass_route", 17) == 0)
+ { p += 17; f.queue_smtp = TRUE; }
+ else
+ break;
cancel_cutthrough_connection(TRUE, US"queueing forced");
break;
{
uschar *endcipher = NULL;
uschar *cipher = Ustrchr(tls_in.cipher, ':');
- if (cipher == NULL) cipher = tls_in.cipher; else
+ if (!cipher) cipher = tls_in.cipher; else
{
endcipher = Ustrchr(++cipher, ':');
- if (endcipher != NULL) *endcipher = 0;
+ if (endcipher) *endcipher = 0;
}
rc = match_isinlist(cipher, &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL);
- if (endcipher != NULL) *endcipher = ':';
+ if (endcipher) *endcipher = ':';
}
break;
case ACLC_HOSTS:
rc = verify_check_this_host(&arg, sender_host_cache, NULL,
- (sender_host_address == NULL)? US"" : sender_host_address,
- CUSS &host_data);
+ sender_host_address ? sender_host_address : US"", CUSS &host_data);
if (rc == DEFER) *log_msgptr = search_error_message;
if (host_data) host_data = string_copy_perm(host_data, TRUE);
break;
for(;;)
{
- while (isspace(*acl_text)) acl_text++; /* Leading spaces/empty lines */
- if (*acl_text == 0) return NULL; /* No more data */
- yield = acl_text; /* Potential data line */
+ Uskip_whitespace(&acl_text); /* Leading spaces/empty lines */
+ if (!*acl_text) return NULL; /* No more data */
+ yield = acl_text; /* Potential data line */
while (*acl_text && *acl_text != '\n') acl_text++;
/* If we hit the end before a newline, we have the whole logical line. If
it's a comment, there's no more data to be given. Otherwise, yield it. */
- if (*acl_text == 0) return (*yield == '#')? NULL : yield;
+ if (!*acl_text) return *yield == '#' ? NULL : yield;
/* After reaching a newline, end this loop if the physical line does not
start with '#'. If it does, it's a comment, and the loop continues. */