tidying
[exim.git] / src / src / acl.c
index 938bea7d9eb13baf55f1928bf09e10d50e36a018..24716f0d10da82133641f471d7fc5d25bdaeb023 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* 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) */
@@ -112,7 +113,8 @@ enum { ACLC_ACL,
 /* 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;
@@ -366,7 +368,7 @@ enum {
   CONTROL_NO_MULTILINE,
   CONTROL_NO_PIPELINING,
 
-  CONTROL_QUEUE_ONLY,
+  CONTROL_QUEUE,
   CONTROL_SUBMISSION,
   CONTROL_SUPPRESS_LOCAL_FIXUPS,
 #ifdef SUPPORT_I18N
@@ -502,8 +504,8 @@ static control_def controls_list[] = {
          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 |
@@ -511,7 +513,6 @@ static control_def controls_list[] = {
            ACL_BIT_NOTSMTP | ACL_BIT_MIME)
   },
 
-
 [CONTROL_SUBMISSION] =
   { US"submission",              TRUE,
          (unsigned)
@@ -732,7 +733,7 @@ uschar * s;
 
 *error = NULL;
 
-while ((s = (*func)()) != NULL)
+while ((s = (*func)()))
   {
   int v, c;
   BOOL negated = FALSE;
@@ -742,8 +743,7 @@ while ((s = (*func)()) != NULL)
   /* 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++;
@@ -859,7 +859,7 @@ while ((s = (*func)()) != NULL)
        }
       cond->u.varname = string_copyn(s, 18);
       s = endptr;
-      while (isspace(*s)) s++;
+      Uskip_whitespace(&s);
       }
     else
 #endif
@@ -895,7 +895,7 @@ while ((s = (*func)()) != NULL)
 
     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
@@ -909,7 +909,7 @@ while ((s = (*func)()) != NULL)
         conditions[c].is_modifier ? US"modifier" : US"condition");
       return NULL;
       }
-    while (isspace(*s)) s++;
+    Uskip_whitespace(&s);
     cond->arg = string_copy(s);
     }
   }
@@ -1515,7 +1515,7 @@ static verify_type_t verify_type_list[] = {
     { 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 },
@@ -1601,7 +1601,7 @@ an error if options are given for items that don't expect them.
 
 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;
@@ -1746,7 +1746,7 @@ switch(vp->value)
     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
       {
@@ -1792,19 +1792,16 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
   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;
@@ -1928,8 +1925,8 @@ else if (verify_sender_address)
     }
 
   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
@@ -1996,14 +1993,12 @@ else if (verify_sender_address)
         *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 */
@@ -2047,8 +2042,7 @@ else
 
   *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;
@@ -2059,8 +2053,10 @@ else
 
 /* 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");
@@ -2070,7 +2066,7 @@ if (rc == DEFER && (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";
@@ -2089,7 +2085,7 @@ if (rc != OK && verify_sender_address != NULL)
 /* 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;
@@ -2113,7 +2109,9 @@ return ERROR;
 *        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=
@@ -2129,10 +2127,11 @@ decode_control(const uschar *arg, const uschar **pptr, int where, uschar **log_m
 {
 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);
@@ -2258,7 +2257,7 @@ count = 1.0;
 
 /* 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;
@@ -3158,8 +3157,17 @@ for (; cb; cb = cb->next)
          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;
 
@@ -3466,13 +3474,13 @@ for (; cb; cb = cb->next)
       {
       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;
 
@@ -3485,8 +3493,7 @@ for (; cb; cb = cb->next)
 
     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;
@@ -3831,16 +3838,16 @@ uschar *yield;
 
 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. */