ACL modifier
[exim.git] / src / src / acl.c
index 1456cc72425fd65a0c1b077eda22d2d07c7adf4b..ec1505bf0023f685ae7c5f301fae005479493b2f 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Code for handling Access Control Lists (ACLs) */
@@ -65,9 +65,6 @@ enum { ACLC_ACL,
        ACLC_DECODE,
 #endif
        ACLC_DELAY,
-#ifdef WITH_OLD_DEMIME
-       ACLC_DEMIME,
-#endif
 #ifndef DISABLE_DKIM
        ACLC_DKIM_SIGNER,
        ACLC_DKIM_STATUS,
@@ -91,6 +88,7 @@ enum { ACLC_ACL,
 #ifdef WITH_CONTENT_SCAN
        ACLC_MIME_REGEX,
 #endif
+       ACLC_QUEUE,
        ACLC_RATELIMIT,
        ACLC_RECIPIENTS,
 #ifdef WITH_CONTENT_SCAN
@@ -111,7 +109,7 @@ enum { ACLC_ACL,
        ACLC_VERIFY };
 
 /* ACL conditions/modifiers: "delay", "control", "continue", "endpass",
-"message", "log_message", "log_reject_target", "logwrite", and "set" are
+"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. */
 
@@ -132,9 +130,6 @@ static uschar *conditions[] = {
   US"decode",
 #endif
   US"delay",
-#ifdef WITH_OLD_DEMIME
-  US"demime",
-#endif
 #ifndef DISABLE_DKIM
   US"dkim_signers",
   US"dkim_status",
@@ -158,13 +153,16 @@ static uschar *conditions[] = {
 #ifdef WITH_CONTENT_SCAN
   US"mime_regex",
 #endif
+  US"queue",
   US"ratelimit",
   US"recipients",
 #ifdef WITH_CONTENT_SCAN
   US"regex",
 #endif
   US"remove_header",
-  US"sender_domains", US"senders", US"set",
+  US"sender_domains",
+  US"senders",
+  US"set",
 #ifdef WITH_CONTENT_SCAN
   US"spam",
 #endif
@@ -281,9 +279,6 @@ static uschar cond_expand_at_top[] = {
   TRUE,    /* decode */
 #endif
   TRUE,    /* delay */
-#ifdef WITH_OLD_DEMIME
-  TRUE,    /* demime */
-#endif
 #ifndef DISABLE_DKIM
   TRUE,    /* dkim_signers */
   TRUE,    /* dkim_status */
@@ -307,6 +302,7 @@ static uschar cond_expand_at_top[] = {
 #ifdef WITH_CONTENT_SCAN
   TRUE,    /* mime_regex */
 #endif
+  TRUE,    /* queue */
   TRUE,    /* ratelimit */
   FALSE,   /* recipients */
 #ifdef WITH_CONTENT_SCAN
@@ -346,9 +342,6 @@ static uschar cond_modifiers[] = {
   FALSE,   /* decode */
 #endif
   TRUE,    /* delay */
-#ifdef WITH_OLD_DEMIME
-  FALSE,   /* demime */
-#endif
 #ifndef DISABLE_DKIM
   FALSE,   /* dkim_signers */
   FALSE,   /* dkim_status */
@@ -372,6 +365,7 @@ static uschar cond_modifiers[] = {
 #ifdef WITH_CONTENT_SCAN
   FALSE,   /* mime_regex */
 #endif
+  TRUE,    /* queue */
   FALSE,   /* ratelimit */
   FALSE,   /* recipients */
 #ifdef WITH_CONTENT_SCAN
@@ -453,15 +447,6 @@ static unsigned int cond_forbids[] = {
 
   (1<<ACL_WHERE_NOTQUIT),                          /* delay */
 
-  #ifdef WITH_OLD_DEMIME
-  (unsigned int)
-  ~((1<<ACL_WHERE_DATA)|                           /* demime */
-  #ifndef DISABLE_PRDR
-    (1<<ACL_WHERE_PRDR)|
-  #endif
-    (1<<ACL_WHERE_NOTSMTP)),
-  #endif
-
   #ifndef DISABLE_DKIM
   (unsigned int)
   ~(1<<ACL_WHERE_DKIM),                            /* dkim_signers */
@@ -482,6 +467,7 @@ static unsigned int cond_forbids[] = {
 
   (unsigned int)
   ~((1<<ACL_WHERE_RCPT)                            /* domains */
+    |(1<<ACL_WHERE_VRFY)
   #ifndef DISABLE_PRDR
     |(1<<ACL_WHERE_PRDR)
   #endif
@@ -499,6 +485,7 @@ static unsigned int cond_forbids[] = {
 
   (unsigned int)
   ~((1<<ACL_WHERE_RCPT)                             /* local_parts */
+    |(1<<ACL_WHERE_VRFY)
   #ifndef DISABLE_PRDR
     |(1<<ACL_WHERE_PRDR)
   #endif
@@ -526,6 +513,7 @@ static unsigned int cond_forbids[] = {
   ~(1<<ACL_WHERE_MIME),                            /* mime_regex */
   #endif
 
+  0,                                               /* queue */
   0,                                               /* ratelimit */
 
   (unsigned int)
@@ -1074,9 +1062,9 @@ while (*hstring == '\n') hstring++, hlen--;
 
 /* An empty string does nothing; ensure exactly one final newline. */
 if (hlen <= 0) return;
-if (hstring[--hlen] != '\n')
+if (hstring[--hlen] != '\n')           /* no newline */
   q = string_sprintf("%s\n", hstring);
-else if (hstring[hlen-1] == '\n')
+else if (hstring[hlen-1] == '\n')      /* double newline */
   {
   uschar * s = string_copy(hstring);
   while(s[--hlen] == '\n')
@@ -1099,7 +1087,7 @@ for (p = q; *p != 0; )
 
   for (;;)
     {
-    q = Ustrchr(q, '\n');
+    q = Ustrchr(q, '\n');              /* we know there was a newline */
     if (*(++q) != ' ' && *q != '\t') break;
     }
 
@@ -1178,11 +1166,11 @@ uschar *
 fn_hdrs_added(void)
 {
 uschar * ret = NULL;
+int size = 0;
+int ptr = 0;
 header_line * h = acl_added_headers;
 uschar * s;
 uschar * cp;
-int size = 0;
-int ptr = 0;
 
 if (!h) return NULL;
 
@@ -1194,13 +1182,13 @@ do
     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);
+    ret = string_catn(ret, &size, &ptr, s, cp-s+1);
+    ret = string_catn(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 */
+  ret = string_catn(ret, &size, &ptr, s, cp-s+1);      /* newline-sep list */
   }
 while((h = h->next));
 
@@ -2374,17 +2362,13 @@ rate measurement as opposed to rate limiting. */
 
 sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0);
 if (sender_rate_limit == NULL)
-  {
-  limit = -1.0;
-  ss = NULL;   /* compiler quietening */
-  }
-else
-  {
-  limit = Ustrtod(sender_rate_limit, &ss);
-  if (tolower(*ss) == 'k') { limit *= 1024.0; ss++; }
-  else if (tolower(*ss) == 'm') { limit *= 1024.0*1024.0; ss++; }
-  else if (tolower(*ss) == 'g') { limit *= 1024.0*1024.0*1024.0; ss++; }
-  }
+  return ratelimit_error(log_msgptr, "sender rate limit not set");
+
+limit = Ustrtod(sender_rate_limit, &ss);
+if      (tolower(*ss) == 'k') { limit *= 1024.0; ss++; }
+else if (tolower(*ss) == 'm') { limit *= 1024.0*1024.0; ss++; }
+else if (tolower(*ss) == 'g') { limit *= 1024.0*1024.0*1024.0; ss++; }
+
 if (limit < 0.0 || *ss != '\0')
   return ratelimit_error(log_msgptr,
     "\"%s\" is not a positive number", sender_rate_limit);
@@ -3535,19 +3519,13 @@ for (; cb != NULL; cb = cb->next)
       }
     break;
 
-    #ifdef WITH_OLD_DEMIME
-    case ACLC_DEMIME:
-      rc = demime(&arg);
-    break;
-    #endif
-
     #ifndef DISABLE_DKIM
     case ACLC_DKIM_SIGNER:
     if (dkim_cur_signer != NULL)
       rc = match_isinlist(dkim_cur_signer,
                           &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL);
     else
-       rc = FAIL;
+      rc = FAIL;
     break;
 
     case ACLC_DKIM_STATUS:
@@ -3706,6 +3684,10 @@ for (; cb != NULL; cb = cb->next)
     break;
     #endif
 
+    case ACLC_QUEUE:
+    queue_name = string_copy_malloc(arg);
+    break;
+
     case ACLC_RATELIMIT:
     rc = acl_ratelimit(arg, where, log_msgptr);
     break;
@@ -4454,9 +4436,9 @@ ratelimiters_cmd = NULL;
 log_reject_target = LOG_MAIN|LOG_REJECT;
 
 #ifndef DISABLE_PRDR
-if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR)
+if (where==ACL_WHERE_RCPT || where==ACL_WHERE_VRFY || where==ACL_WHERE_PRDR)
 #else
-if (where == ACL_WHERE_RCPT)
+if (where==ACL_WHERE_RCPT || where==ACL_WHERE_VRFY)
 #endif
   {
   adb = address_defaults;
@@ -4487,8 +4469,8 @@ and WHERE_RCPT and not yet opened conn as result of recipient-verify,
 and rcpt acl returned accept,
 and first recipient (cancel on any subsequents)
 open one now and run it up to RCPT acceptance.
-A failed verify should cancel cutthrough request.
-
+A failed verify should cancel cutthrough request,
+and will pass the fail to the originator.
 Initial implementation:  dual-write to spool.
 Assume the rxd datastream is now being copied byte-for-byte to an open cutthrough connection.
 
@@ -4507,12 +4489,14 @@ case ACL_WHERE_RCPT:
 #ifndef DISABLE_PRDR
 case ACL_WHERE_PRDR:
 #endif
-  if (rc == OK  &&  cutthrough.delivery  && rcpt_count > cutthrough.nrcpt)
-    open_cutthrough_connection(addr);
+  if (host_checking_callout)   /* -bhc mode */
+    cancel_cutthrough_connection("host-checking mode");
+  else if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt)
+    rc = open_cutthrough_connection(addr);
   break;
 
 case ACL_WHERE_PREDATA:
-  if( rc == OK )
+  if (rc == OK)
     cutthrough_predata();
   else
     cancel_cutthrough_connection("predata acl not ok");