Fix text type in ACL error message.
[exim.git] / src / src / acl.c
index 8274e0c73f00704467b8d985284ecb25ce82d736..277ea8d4f64485310234d92853e26a1689a851ba 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/acl.c,v 1.66 2006/09/25 10:14:20 ph10 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.76 2007/06/19 13:32:06 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2006 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Code for handling Access Control Lists (ACLs) */
@@ -27,9 +27,20 @@ static uschar *verbs[] =
   { 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, and the cond_expand_at_top and uschar cond_modifiers tables lower
@@ -42,6 +53,7 @@ enum { ACLC_ACL,
        ACLC_BMI_OPTIN,
 #endif
        ACLC_CONDITION,
+       ACLC_CONTINUE,
        ACLC_CONTROL,
 #ifdef WITH_CONTENT_SCAN
        ACLC_DECODE,
@@ -90,10 +102,10 @@ enum { ACLC_ACL,
 #endif
        ACLC_VERIFY };
 
-/* ACL conditions/modifiers: "delay", "control", "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. */
+/* 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",
@@ -103,6 +115,7 @@ static uschar *conditions[] = {
   US"bmi_optin",
 #endif
   US"condition",
+  US"continue",
   US"control",
 #ifdef WITH_CONTENT_SCAN
   US"decode",
@@ -175,7 +188,10 @@ enum {
   #endif
   CONTROL_FAKEDEFER,
   CONTROL_FAKEREJECT,
-  CONTROL_NO_MULTILINE
+  CONTROL_NO_MULTILINE,
+  CONTROL_NO_PIPELINING,
+  CONTROL_NO_DELAY_FLUSH,
+  CONTROL_NO_CALLOUT_FLUSH
 };
 
 /* ACL control names; keep in step with the table above! This list is used for
@@ -203,10 +219,15 @@ static uschar *controls[] = {
   #ifdef WITH_CONTENT_SCAN
   US"no_mbox_unspool",
   #endif
-  US"no_multiline"
+  US"fakedefer",
+  US"fakereject",
+  US"no_multiline_responses",
+  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. */
 
@@ -218,6 +239,7 @@ static uschar cond_expand_at_top[] = {
   TRUE,    /* bmi_optin */
 #endif
   TRUE,    /* condition */
+  TRUE,    /* continue */
   TRUE,    /* control */
 #ifdef WITH_CONTENT_SCAN
   TRUE,    /* decode */
@@ -277,6 +299,7 @@ static uschar cond_modifiers[] = {
   TRUE,    /* bmi_optin */
 #endif
   FALSE,   /* condition */
+  TRUE,    /* continue */
   TRUE,    /* control */
 #ifdef WITH_CONTENT_SCAN
   FALSE,   /* decode */
@@ -326,9 +349,9 @@ static uschar cond_modifiers[] = {
   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 */
@@ -356,6 +379,8 @@ static unsigned int cond_forbids[] = {
 
   0,                                               /* condition */
 
+  0,                                               /* continue */
+
   /* Certain types of control are always allowed, so we let it through
   always and check in the control processing itself. */
 
@@ -575,6 +600,15 @@ static unsigned int control_forbids[] = {
     (1<<ACL_WHERE_MIME)),
 
   (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)
 };
 
@@ -598,8 +632,11 @@ static control_def controls_list[] = {
   { 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 },
@@ -1939,10 +1976,13 @@ else
     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;
@@ -2523,6 +2563,9 @@ for (; cb != NULL; cb = cb->next)
       *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);
 
@@ -2582,6 +2625,18 @@ for (; cb != NULL; cb = cb->next)
       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;
@@ -2697,13 +2752,18 @@ for (; cb != NULL; cb = cb->next)
         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);
           }
         }
@@ -3035,13 +3095,8 @@ for (; cb != NULL; cb = cb->next)
 
 
 /* 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.
@@ -3049,23 +3104,29 @@ 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 (*epp && rc == OK) user_message = NULL;
 
-if (((rc == FAIL_DROP)? FAIL : rc) == msgcond[verb] ||
-    (verb == ACL_DISCARD && rc == OK) ||
-    (where == ACL_WHERE_QUIT))
+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)
     {
@@ -3595,51 +3656,12 @@ if (rc == FAIL_DROP && where == ACL_WHERE_MAILAUTH)
   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. */
-
-if (rc != OK && *user_msgptr != NULL && Ustrlen(*user_msgptr) > 75)
-  {
-  uschar *s = *user_msgptr = string_copy(*user_msgptr);
-  uschar *ss = s;
+/* Before giving a response, take a look at the length of any user message, and
+split it up into multiple lines if possible. */
 
-  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;
-          }
-        }
-
-      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;
-      }
-    }
-  }
+*user_msgptr = string_split_message(*user_msgptr);
+if (fake_response != OK)
+  fake_response_text = string_split_message(fake_response_text);
 
 return rc;
 }