Applied a modified version of Brad Jorsch's patch for "message" with
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Tue, 14 Nov 2006 16:40:36 +0000 (16:40 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Tue, 14 Nov 2006 16:40:36 +0000 (16:40 +0000)
"accept".

16 files changed:
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/ACKNOWLEDGMENTS
src/src/acl.c
src/src/functions.h
src/src/receive.c
src/src/smtp_in.c
test/confs/0023
test/confs/0546 [new file with mode: 0644]
test/log/0546 [new file with mode: 0644]
test/paniclog/0546 [new file with mode: 0644]
test/runtest
test/scripts/0000-Basic/0546 [new file with mode: 0644]
test/stderr/0546 [new file with mode: 0644]
test/stdout/0023
test/stdout/0546 [new file with mode: 0644]

index 01ff8dc..295ae7b 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.433 2006/11/13 12:29:30 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.434 2006/11/14 16:40:36 ph10 Exp $
 
 Change log file for Exim from version 4.21
 -------------------------------------------
@@ -274,6 +274,9 @@ PH/43 Renamed the variables $interface_address and $interface_port as
 PH/44 There was no timeout on the connect() call when using a Unix domain
       socket in the ${readsocket expansion. There now is.
 
+PH/45 Applied a modified version of Brad Jorsch's patch to allow "message" to
+      be meaningful with "accept".
+
 
 Exim version 4.63
 -----------------
index 564d76d..67901a2 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.122 2006/11/13 12:07:46 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.123 2006/11/14 16:40:36 ph10 Exp $
 
 New Features in Exim
 --------------------
@@ -197,6 +197,51 @@ Version 4.64
     relate to message reception rather than delivery. (The old names remain
     available for compatibility.)
 
+12. The "message" modifier can now be used on acl verbs to vary the message
+    that is sent when an SMTP command. For example, in a RCPT ACL you could
+    have:
+
+      accept  <some conditions>
+              message = OK, I'll allow you through today
+
+    Previously, this message modifier would have had no effect whatsoever.
+
+    IMPORTANT: The new behaviour applies to "accept" (and "discard") only if
+    there is no occurrence of "endpass" in the statement. If "endpass" is
+    present, the behaviour reverts to the old case, where "message" applies to
+    rejection. This is for backwards compatibility.
+
+    It is always possible to rewrite ACL statements so that "endpass" is not
+    needed (and indeed it is no longer used in the default configuration, and
+    is somewhat not recommended nowadays because it causes confusion.)
+
+    It is now generally true that the "message" modifier sets up a text string
+    that is expanded and used as a response message if the current statement
+    terminates the ACL. The expansion happens at the time Exim decides that the
+    ACL is to end, not at the time it processes "message". If the expansion
+    fails, or generates an empty string, the modifier is ignored.
+
+    For ACLs that are triggered by SMTP commands, the message is returned as
+    part of the SMTP response. In this situation, the message may begin with an
+    overriding SMTP response code, optionally followed by an "extended response
+    code". However, the first digit of the supplied response code must be the
+    same as would be sent by default. A panic occurs if it is not. For the
+    predata ACL, note that the default success code is 354, not 2xx.
+
+    However, notwithstanding the previous paragraph, for the QUIT ACL, unlike
+    the others, the message modifier cannot override the 221 response code.
+
+    In the case of the "connect" ACL, accepting with a message modifier
+    overrides the value of smtp_banner.
+
+    The ACL test specified by acl_smtp_helo happens when the client issues the
+    HELO or EHLO commands, after the tests specified by helo_accept_junk_hosts,
+    helo_allow_chars and helo(_try)_verify_hosts. An acceptance message
+    modifier for EHLO/HELO may not contain more than one line (it will be
+    truncated at the first newline and a panic logged), and it cannot affect
+    the EHLO options.
+
+
 
 Version 4.63
 ------------
index 2491260..b2552d7 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.63 2006/11/13 11:26:37 ph10 Exp $
+$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.64 2006/11/14 16:40:36 ph10 Exp $
 
 EXIM ACKNOWLEDGEMENTS
 
@@ -20,7 +20,7 @@ relatively small patches.
 Philip Hazel
 
 Lists created: 20 November 2002
-Last updated:  13 November 2006
+Last updated:  14 November 2006
 
 
 THE OLD LIST
@@ -174,6 +174,7 @@ John Jetmore              Writing and maintaining the 'exipick' utility
 Bob Johannessen           Patch for Sieve envelope tests bug
                           Patch for negative uid/gid bug
 Brad Jorsch               Patch for bitwise logical operators
+                          Patch for using "message" on acceptance
 Christian Kellner         Patch for LDAP dereferencing
 Alex Kiernan              Patches for libradius
                           Diagnosis of milliwait clock-backwards bug
index 8274e0c..a79c31c 100644 (file)
@@ -1,4 +1,4 @@
-/* $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.67 2006/11/14 16:40:36 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -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
@@ -3035,13 +3046,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 +3055,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 (((rc == FAIL_DROP)? FAIL : rc) == msgcond[verb] ||
-    (verb == ACL_DISCARD && rc == OK) ||
-    (where == ACL_WHERE_QUIT))
+if (*epp && rc == OK) user_message = NULL;
+
+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,10 +3607,10 @@ 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. */
+/* Before giving a 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)
+if (*user_msgptr != NULL && Ustrlen(*user_msgptr) > 75)
   {
   uschar *s = *user_msgptr = string_copy(*user_msgptr);
   uschar *ss = s;
index b91ca27..0e80605 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/functions.h,v 1.30 2006/10/24 12:56:06 ph10 Exp $ */
+/* $Cambridge: exim/src/src/functions.h,v 1.31 2006/11/14 16:40:36 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -269,6 +269,7 @@ extern BOOL    smtp_get_interface(uschar *, int, address_item *, BOOL *,
 extern BOOL    smtp_get_port(uschar *, address_item *, int *, uschar *);
 extern int     smtp_getc(void);
 extern int     smtp_handle_acl_fail(int, int, uschar *, uschar *);
+extern void    smtp_message_code(uschar **, int *, uschar **, uschar **);
 extern BOOL    smtp_read_response(smtp_inblock *, uschar *, int, int, int);
 extern void    smtp_respond(uschar *, int, BOOL, uschar *);
 extern void    smtp_send_prohibition_message(int, uschar *);
index 73675be..8fba1ca 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/receive.c,v 1.30 2006/10/10 15:36:50 ph10 Exp $ */
+/* $Cambridge: exim/src/src/receive.c,v 1.31 2006/11/14 16:40:36 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -1042,18 +1042,21 @@ memset(CS rfc822_file_path,0,2048);
 
 /* check if it is a MIME message */
 my_headerlist = header_list;
-while (my_headerlist != NULL) {
+while (my_headerlist != NULL)
+  {
   /* skip deleted headers */
-  if (my_headerlist->type == '*') {
+  if (my_headerlist->type == '*')
+    {
     my_headerlist = my_headerlist->next;
     continue;
-  };
-  if (strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0) {
+    }
+  if (strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0)
+    {
     DEBUG(D_receive) debug_printf("Found Content-Type: header - executing acl_smtp_mime.\n");
     goto DO_MIME_ACL;
-  };
+    }
   my_headerlist = my_headerlist->next;
-};
+  }
 
 DEBUG(D_receive) debug_printf("No Content-Type: header - presumably not a MIME message.\n");
 return TRUE;
@@ -1080,18 +1083,21 @@ mime_part_count = -1;
 rc = mime_acl_check(acl, mbox_file, NULL, &user_msg, &log_msg);
 (void)fclose(mbox_file);
 
-if (Ustrlen(rfc822_file_path) > 0) {
+if (Ustrlen(rfc822_file_path) > 0)
+  {
   mime_part_count = mime_part_count_buffer;
 
-  if (unlink(CS rfc822_file_path) == -1) {
+  if (unlink(CS rfc822_file_path) == -1)
+    {
     log_write(0, LOG_PANIC,
          "acl_smtp_mime: can't unlink RFC822 spool file, skipping.");
       goto END_MIME_ACL;
-  };
-};
+    }
+  }
 
 /* check if we must check any message/rfc822 attachments */
-if (rc == OK) {
+if (rc == OK)
+  {
   uschar temp_path[1024];
   int n;
   struct dirent *entry;
@@ -1100,33 +1106,37 @@ if (rc == OK) {
   (void)string_format(temp_path, 1024, "%s/scan/%s", spool_directory,
     message_id);
 
- tempdir = opendir(CS temp_path);
- n = 0;
- do {
-   entry = readdir(tempdir);
-   if (entry == NULL) break;
-    if (strncmpic(US entry->d_name,US"__rfc822_",9) == 0) {
+  tempdir = opendir(CS temp_path);
+  n = 0;
+  do
+    {
+    entry = readdir(tempdir);
+    if (entry == NULL) break;
+    if (strncmpic(US entry->d_name,US"__rfc822_",9) == 0)
+      {
       (void)string_format(rfc822_file_path, 2048,"%s/scan/%s/%s", spool_directory, message_id, entry->d_name);
-     debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path);
-     break;
-    };
- } while (1);
- closedir(tempdir);
+      debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path);
+      break;
+      }
   } while (1);
 closedir(tempdir);
 
-  if (entry != NULL) {
+  if (entry != NULL)
+    {
     mbox_file = Ufopen(rfc822_file_path,"rb");
-    if (mbox_file == NULL) {
+    if (mbox_file == NULL)
+      {
       log_write(0, LOG_PANIC,
          "acl_smtp_mime: can't open RFC822 spool file, skipping.");
       unlink(CS rfc822_file_path);
       goto END_MIME_ACL;
-    };
+      }
     /* set RFC822 expansion variable */
     mime_is_rfc822 = 1;
     mime_part_count_buffer = mime_part_count;
     goto MIME_ACL_CHECK;
-  };
-};
+    }
+  }
 
 END_MIME_ACL:
 add_acl_headers(US"MIME");
@@ -1144,7 +1154,7 @@ else if (rc != OK)
   *smtp_reply_ptr = US"";       /* Indicate reply already sent */
   message_id[0] = 0;            /* Indicate no message accepted */
   return FALSE;                 /* Cause skip to end of receive function */
-  };
+  }
 
 return TRUE;
 }
@@ -1276,9 +1286,10 @@ uschar *queued_by = NULL;
 uschar *errmsg, *s;
 struct stat statbuf;
 
-/* Final message to give to SMTP caller */
+/* Final message to give to SMTP caller, and messages from ACLs */
 
 uschar *smtp_reply = NULL;
+uschar *user_msg, *log_msg;
 
 /* Working header pointers */
 
@@ -2902,6 +2913,7 @@ $message_body_end can be extracted if needed. Allow $recipients in expansions.
 */
 
 deliver_datafile = data_fd;
+user_msg = NULL;
 
 if (recipients_count == 0)
   {
@@ -2931,7 +2943,6 @@ else
 
     if (acl_smtp_data != NULL && recipients_count > 0)
       {
-      uschar *user_msg, *log_msg;
       rc = acl_check(ACL_WHERE_DATA, NULL, acl_smtp_data, &user_msg, &log_msg);
       add_acl_headers(US"DATA");
       if (rc == DISCARD)
@@ -3491,12 +3502,28 @@ if (smtp_input)
       if (fake_response != OK)
         smtp_respond((fake_response == DEFER)? US"450" : US"550", 3, TRUE,
           fake_response_text);
+
+      /* An OK response is required; use "message" text if present. */
+
+      else if (user_msg != NULL)
+        {
+        uschar *code = US"250";
+        int len = 3;
+        smtp_message_code(&code, &len, &user_msg, NULL);
+        smtp_respond(code, len, TRUE, user_msg);
+        }
+
+      /* Default OK response */
+
       else
         smtp_printf("250 OK id=%s\r\n", message_id);
       if (host_checking)
         fprintf(stdout,
           "\n**** SMTP testing: that is not a real message id!\n\n");
       }
+
+    /* smtp_reply was previously set */
+
     else if (smtp_reply[0] != 0)
       {
       if (fake_response != OK && (smtp_reply[0] == '2'))
index c75b2b2..97b721e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.46 2006/10/23 10:55:10 ph10 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.47 2006/11/14 16:40:36 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -874,7 +874,7 @@ if (message_body_end != NULL)
 
 /* Warning log messages are also saved in malloc store. They are saved to avoid
 repetition in the same message, but it seems right to repeat them for different
-messagess. */
+messages. */
 
 while (acl_warn_logged != NULL)
   {
@@ -1141,7 +1141,9 @@ BOOL
 smtp_start_session(void)
 {
 int size = 256;
-int ptr;
+int ptr, esclen;
+uschar *user_msg, *log_msg;
+uschar *code, *esc;
 uschar *p, *s, *ss;
 
 /* Default values for certain variables */
@@ -1571,10 +1573,10 @@ if (smtp_batched_input) return TRUE;
 
 /* Run the ACL if it exists */
 
+user_msg = NULL;
 if (acl_smtp_connect != NULL)
   {
   int rc;
-  uschar *user_msg, *log_msg;
   rc = acl_check(ACL_WHERE_CONNECT, NULL, acl_smtp_connect, &user_msg,
     &log_msg);
   if (rc != OK)
@@ -1587,10 +1589,28 @@ if (acl_smtp_connect != NULL)
 /* Output the initial message for a two-way SMTP connection. It may contain
 newlines, which then cause a multi-line response to be given. */
 
-s = expand_string(smtp_banner);
-if (s == NULL)
-  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" (smtp_banner) "
-    "failed: %s", smtp_banner, expand_string_message);
+code = US"220";   /* Default status code */
+esc = US"";       /* Default extended status code */
+esclen = 0;       /* Length of esc */
+
+if (user_msg == NULL)
+  {
+  s = expand_string(smtp_banner);
+  if (s == NULL)
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" (smtp_banner) "
+      "failed: %s", smtp_banner, expand_string_message);
+  }
+else
+  {
+  int codelen = 3;
+  s = user_msg;
+  smtp_message_code(&code, &codelen, &s, NULL);
+  if (codelen > 3)
+    {
+    esc = code + 4;
+    esclen = codelen - 4;
+    }
+  }
 
 /* Remove any terminating newlines; might as well remove trailing space too */
 
@@ -1615,16 +1635,18 @@ do       /* At least once, in case we have an empty string */
   {
   int len;
   uschar *linebreak = Ustrchr(p, '\n');
+  ss = string_cat(ss, &size, &ptr, code, 3);
   if (linebreak == NULL)
     {
     len = Ustrlen(p);
-    ss = string_cat(ss, &size, &ptr, US"220 ", 4);
+    ss = string_cat(ss, &size, &ptr, US" ", 1);
     }
   else
     {
     len = linebreak - p;
-    ss = string_cat(ss, &size, &ptr, US"220-", 4);
+    ss = string_cat(ss, &size, &ptr, US"-", 1);
     }
+  ss = string_cat(ss, &size, &ptr, esc, esclen);
   ss = string_cat(ss, &size, &ptr, p, len);
   ss = string_cat(ss, &size, &ptr, US"\r\n", 2);
   p += len;
@@ -1771,7 +1793,7 @@ output nothing for non-final calls, and only the first line for anything else.
 
 Arguments:
   code          SMTP code, may involve extended status codes
-  codelen       length of smtp code; uf > 3 there's an ESC
+  codelen       length of smtp code; if > 3 there's an ESC
   final         FALSE if the last line isn't the final line
   msg           message text, possibly containing newlines
 
@@ -1819,6 +1841,62 @@ for (;;)
 
 
 /*************************************************
+*            Parse user SMTP message             *
+*************************************************/
+
+/* This function allows for user messages overriding the response code details
+by providing a suitable response code string at the start of the message
+user_msg. Check the message for starting with a response code and optionally an
+extended status code. If found, check that the first digit is valid, and if so,
+change the code pointer and length to use the replacement. An invalid code
+causes a panic log; in this case, if the log messages is the same as the user
+message, we must also adjust the value of the log message to show the code that
+is actually going to be used (the original one).
+
+This function is global because it is called from receive.c as well as within
+this module.
+
+Arguments:
+  code          SMTP code, may involve extended status codes
+  codelen       length of smtp code; if > 3 there's an ESC
+  msg           message text
+  log_msg       optional log message, to be adjusted with the new SMTP code
+
+Returns:        nothing
+*/
+
+void
+smtp_message_code(uschar **code, int *codelen, uschar **msg, uschar **log_msg)
+{
+int n;
+int ovector[3];
+
+if (msg == NULL || *msg == NULL) return;
+
+n = pcre_exec(regex_smtp_code, NULL, CS *msg, Ustrlen(*msg), 0,
+  PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int));
+if (n < 0) return;
+
+if ((*msg)[0] != (*code)[0])
+  {
+  log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with "
+    "incorrect digit (expected %c) in \"%s\"", (*code)[0], *msg);
+  if (log_msg != NULL && *log_msg == *msg)
+    *log_msg = string_sprintf("%s %s", *code, *log_msg + ovector[1]);
+  }
+else
+  {
+  *code = *msg;
+  *codelen = ovector[1];    /* Includes final space */
+  }
+*msg += ovector[1];         /* Chop the code off the message */
+return;
+}
+
+
+
+
+/*************************************************
 *           Handle an ACL failure                *
 *************************************************/
 
@@ -1858,7 +1936,6 @@ smtp_handle_acl_fail(int where, int rc, uschar *user_msg, uschar *log_msg)
 {
 BOOL drop = rc == FAIL_DROP;
 int codelen = 3;
-int ovector[3];
 uschar *smtp_code;
 uschar *lognl;
 uschar *sender_info = US"";
@@ -1874,40 +1951,10 @@ uschar *what =
 
 if (drop) rc = FAIL;
 
-/* Set the default SMTP code */
+/* Set the default SMTP code, and allow a user message to change it. */
 
 smtp_code = (rc != FAIL)? US"451" : acl_wherecodes[where];
-
-/* Check a user message for starting with a response code and optionally an
-extended status code. If found, check that the first digit is valid, and if so,
-use it instead of the default code. */
-
-if (user_msg != NULL)
-  {
-  int n = pcre_exec(regex_smtp_code, NULL, CS user_msg, Ustrlen(user_msg), 0,
-    PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int));
-  if (n >= 0)
-    {
-    if (user_msg[0] != smtp_code[0])
-      {
-      log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with "
-        "incorrect digit (expected %c) in \"%s\"", smtp_code[0], user_msg);
-
-      /* If log_msg == user_msg (the default set in acl.c if no log message is
-      specified, we must adjust the log message to show the code that is
-      actually going to be used. */
-
-      if (log_msg == user_msg)
-        log_msg = string_sprintf("%s %s", smtp_code, log_msg + ovector[1]);
-      }
-    else
-      {
-      smtp_code = user_msg;
-      codelen = ovector[1];    /* Includes final space */
-      }
-    user_msg += ovector[1];    /* Chop the code off the message */
-    }
-  }
+smtp_message_code(&smtp_code, &codelen, &user_msg, &log_msg);
 
 /* We used to have sender_address here; however, there was a bug that was not
 updating sender_address after a rewrite during a verify. When this bug was
@@ -2157,6 +2204,33 @@ return yield;
 
 
 /*************************************************
+*        Send user response message              *
+*************************************************/
+
+/* This function is passed a default response code and a user message. It calls
+smtp_message_code() to check and possibly modify the response code, and then
+calls smtp_respond() to transmit the response. I put this into a function
+just to avoid a lot of repetition.
+
+Arguments:
+  code         the response code
+  user_msg     the user message
+
+Returns:       nothing
+*/
+
+static void
+smtp_user_msg(uschar *code, uschar *user_msg)
+{
+int len = 3;
+smtp_message_code(&code, &len, &user_msg, NULL);
+smtp_respond(code, len, TRUE, user_msg);
+}
+
+
+
+
+/*************************************************
 *       Initialize for SMTP incoming message     *
 *************************************************/
 
@@ -2226,7 +2300,8 @@ while (done <= 0)
   uschar *etrn_command;
   uschar *etrn_serialize_key;
   uschar *errmess;
-  uschar *user_msg, *log_msg;
+  uschar *log_msg, *smtp_code;
+  uschar *user_msg = NULL;
   uschar *recipient = NULL;
   uschar *hello = NULL;
   uschar *set_id = NULL;
@@ -2563,26 +2638,11 @@ while (done <= 0)
         }
       }
 
-    /* The EHLO/HELO command is acceptable. Reset the protocol and the state,
-    abandoning any previous message. */
-
-    received_protocol = (esmtp?
-      protocols[pextend +
-        ((sender_host_authenticated != NULL)? pauthed : 0) +
-        ((tls_active >= 0)? pcrpted : 0)]
-      :
-      protocols[pnormal + ((tls_active >= 0)? pcrpted : 0)])
-      +
-      ((sender_host_address != NULL)? pnlocal : 0);
-
-    smtp_reset(reset_point);
-    toomany = FALSE;
-
-    /* Generate an OK reply, including the ident if present, and also
-    the IP address if present. Reflecting back the ident is intended
-    as a deterrent to mail forgers. For maximum efficiency, and also
-    because some broken systems expect each response to be in a single
-    packet, arrange that it is sent in one write(). */
+    /* Generate an OK reply. The default string includes the ident if present,
+    and also the IP address if present. Reflecting back the ident is intended
+    as a deterrent to mail forgers. For maximum efficiency, and also because
+    some broken systems expect each response to be in a single packet, arrange
+    that the entire reply is sent in one write(). */
 
     auth_advertised = FALSE;
     pipelining_advertised = FALSE;
@@ -2590,21 +2650,44 @@ while (done <= 0)
     tls_advertised = FALSE;
     #endif
 
-    s = string_sprintf("250 %s Hello %s%s%s",
-      smtp_active_hostname,
-      (sender_ident == NULL)?  US"" : sender_ident,
-      (sender_ident == NULL)?  US"" : US" at ",
-      (sender_host_name == NULL)? sender_helo_name : sender_host_name);
+    smtp_code = US"250";        /* Default response code */
+    if (user_msg == NULL)
+      {
+      s = string_sprintf("%.3s %s Hello %s%s%s",
+        smtp_code,
+        smtp_active_hostname,
+        (sender_ident == NULL)?  US"" : sender_ident,
+        (sender_ident == NULL)?  US"" : US" at ",
+        (sender_host_name == NULL)? sender_helo_name : sender_host_name);
+
+      ptr = Ustrlen(s);
+      size = ptr + 1;
 
-    ptr = Ustrlen(s);
-    size = ptr + 1;
+      if (sender_host_address != NULL)
+        {
+        s = string_cat(s, &size, &ptr, US" [", 2);
+        s = string_cat(s, &size, &ptr, sender_host_address,
+          Ustrlen(sender_host_address));
+        s = string_cat(s, &size, &ptr, US"]", 1);
+        }
+      }
+
+    /* A user-supplied EHLO greeting may not contain more than one line */
 
-    if (sender_host_address != NULL)
+    else
       {
-      s = string_cat(s, &size, &ptr, US" [", 2);
-      s = string_cat(s, &size, &ptr, sender_host_address,
-        Ustrlen(sender_host_address));
-      s = string_cat(s, &size, &ptr, US"]", 1);
+      char *ss;
+      int codelen = 3;
+      smtp_message_code(&smtp_code, &codelen, &user_msg, NULL);
+      s = string_sprintf("%.*s %s", codelen, smtp_code, user_msg);
+      if ((ss = strpbrk(CS s, "\r\n")) != NULL)
+        {
+        log_write(0, LOG_MAIN|LOG_PANIC, "EHLO/HELO response must not contain "
+          "newlines: message truncated: %s", string_printing(s));
+        *ss = 0;
+        }
+      ptr = Ustrlen(s);
+      size = ptr + 1;
       }
 
     s = string_cat(s, &size, &ptr, US"\r\n", 2);
@@ -2624,12 +2707,14 @@ while (done <= 0)
 
       if (thismessage_size_limit > 0)
         {
-        sprintf(CS big_buffer, "250-SIZE %d\r\n", thismessage_size_limit);
+        sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code,
+          thismessage_size_limit);
         s = string_cat(s, &size, &ptr, big_buffer, Ustrlen(big_buffer));
         }
       else
         {
-        s = string_cat(s, &size, &ptr, US"250-SIZE\r\n", 10);
+        s = string_cat(s, &size, &ptr, smtp_code, 3);
+        s = string_cat(s, &size, &ptr, US"-SIZE\r\n", 7);
         }
 
       /* Exim does not do protocol conversion or data conversion. It is 8-bit
@@ -2640,14 +2725,18 @@ while (done <= 0)
       provided as an option. */
 
       if (accept_8bitmime)
-        s = string_cat(s, &size, &ptr, US"250-8BITMIME\r\n", 14);
+        {
+        s = string_cat(s, &size, &ptr, smtp_code, 3);
+        s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11);
+        }
 
       /* Advertise ETRN if there's an ACL checking whether a host is
       permitted to issue it; a check is made when any host actually tries. */
 
       if (acl_smtp_etrn != NULL)
         {
-        s = string_cat(s, &size, &ptr, US"250-ETRN\r\n", 10);
+        s = string_cat(s, &size, &ptr, smtp_code, 3);
+        s = string_cat(s, &size, &ptr, US"-ETRN\r\n", 7);
         }
 
       /* Advertise EXPN if there's an ACL checking whether a host is
@@ -2655,7 +2744,8 @@ while (done <= 0)
 
       if (acl_smtp_expn != NULL)
         {
-        s = string_cat(s, &size, &ptr, US"250-EXPN\r\n", 10);
+        s = string_cat(s, &size, &ptr, smtp_code, 3);
+        s = string_cat(s, &size, &ptr, US"-EXPN\r\n", 7);
         }
 
       /* Exim is quite happy with pipelining, so let the other end know that
@@ -2663,7 +2753,8 @@ while (done <= 0)
 
       if (verify_check_host(&pipelining_advertise_hosts) == OK)
         {
-        s = string_cat(s, &size, &ptr, US"250-PIPELINING\r\n", 16);
+        s = string_cat(s, &size, &ptr, smtp_code, 3);
+        s = string_cat(s, &size, &ptr, US"-PIPELINING\r\n", 13);
         sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
         pipelining_advertised = TRUE;
         }
@@ -2693,7 +2784,8 @@ while (done <= 0)
               int saveptr;
               if (first)
                 {
-                s = string_cat(s, &size, &ptr, US"250-AUTH", 8);
+                s = string_cat(s, &size, &ptr, smtp_code, 3);
+                s = string_cat(s, &size, &ptr, US"-AUTH", 5);
                 first = FALSE;
                 auth_advertised = TRUE;
                 }
@@ -2719,14 +2811,16 @@ while (done <= 0)
       if (tls_active < 0 &&
           verify_check_host(&tls_advertise_hosts) != FAIL)
         {
-        s = string_cat(s, &size, &ptr, US"250-STARTTLS\r\n", 14);
+        s = string_cat(s, &size, &ptr, smtp_code, 3);
+        s = string_cat(s, &size, &ptr, US"-STARTTLS\r\n", 11);
         tls_advertised = TRUE;
         }
       #endif
 
       /* Finish off the multiline reply with one that is always available. */
 
-      s = string_cat(s, &size, &ptr, US"250 HELP\r\n", 10);
+      s = string_cat(s, &size, &ptr, smtp_code, 3);
+      s = string_cat(s, &size, &ptr, US" HELP\r\n", 7);
       }
 
     /* Terminate the string (for debug), write it, and note that HELO/EHLO
@@ -2747,6 +2841,20 @@ while (done <= 0)
       debug_printf("SMTP>> %s", s);
       }
     helo_seen = TRUE;
+
+    /* Reset the protocol and the state, abandoning any previous message. */
+
+    received_protocol = (esmtp?
+      protocols[pextend +
+        ((sender_host_authenticated != NULL)? pauthed : 0) +
+        ((tls_active >= 0)? pcrpted : 0)]
+      :
+      protocols[pnormal + ((tls_active >= 0)? pcrpted : 0)])
+      +
+      ((sender_host_address != NULL)? pnlocal : 0);
+
+    smtp_reset(reset_point);
+    toomany = FALSE;
     break;   /* HELO/EHLO */
 
 
@@ -3024,12 +3132,12 @@ while (done <= 0)
 
     if (rc == OK || rc == DISCARD)
       {
-      smtp_printf("250 OK\r\n");
+      if (user_msg == NULL) smtp_printf("250 OK\r\n");
+        else smtp_user_msg(US"250", user_msg);
       smtp_delay_rcpt = smtp_rlr_base;
       recipients_discarded = (rc == DISCARD);
       was_rej_mail = FALSE;
       }
-
     else
       {
       done = smtp_handle_acl_fail(ACL_WHERE_MAIL, rc, user_msg, log_msg);
@@ -3184,7 +3292,8 @@ while (done <= 0)
 
     if (rc == OK)
       {
-      smtp_printf("250 Accepted\r\n");
+      if (user_msg == NULL) smtp_printf("250 Accepted\r\n");
+        else smtp_user_msg(US"250", user_msg);
       receive_add_recipient(recipient, -1);
       }
 
@@ -3192,7 +3301,8 @@ while (done <= 0)
 
     else if (rc == DISCARD)
       {
-      smtp_printf("250 Accepted\r\n");
+      if (user_msg == NULL) smtp_printf("250 Accepted\r\n");
+        else smtp_user_msg(US"250", user_msg);
       rcpt_fail_count++;
       discarded = TRUE;
       log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> rejected RCPT %s: "
@@ -3259,7 +3369,9 @@ while (done <= 0)
 
     if (rc == OK)
       {
-      smtp_printf("354 Enter message, ending with \".\" on a line by itself\r\n");
+      if (user_msg == NULL)
+        smtp_printf("354 Enter message, ending with \".\" on a line by itself\r\n");
+      else smtp_user_msg(US"354", user_msg);
       done = 3;
       message_ended = END_NOTENDED;   /* Indicate in middle of data */
       }
@@ -3461,12 +3573,11 @@ while (done <= 0)
         log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
           log_msg);
       }
-    else user_msg = NULL;
 
     if (user_msg == NULL)
       smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
     else
-      smtp_printf("221 %s\r\n", user_msg);
+      smtp_respond(US"221", 3, TRUE, user_msg);
 
     #ifdef SUPPORT_TLS
     tls_close(TRUE);
@@ -3606,7 +3717,8 @@ while (done <= 0)
         debug_printf("ETRN command is: %s\n", etrn_command);
         debug_printf("ETRN command execution skipped\n");
         }
-      smtp_printf("250 OK\r\n");
+      if (user_msg == NULL) smtp_printf("250 OK\r\n");
+        else smtp_user_msg(US"250", user_msg);
       break;
       }
 
@@ -3682,7 +3794,11 @@ while (done <= 0)
       smtp_printf("458 Unable to fork process\r\n");
       if (smtp_etrn_serialize) enq_end(etrn_serialize_key);
       }
-    else smtp_printf("250 OK\r\n");
+    else
+      {
+      if (user_msg == NULL) smtp_printf("250 OK\r\n");
+        else smtp_user_msg(US"250", user_msg);
+      }
 
     signal(SIGCHLD, oldsignal);
     break;
index be99eb0..ac6d8f7 100644 (file)
@@ -203,7 +203,7 @@ acl_56_56_56:
   accept
 
 acl_56_56_57:
-  accept  message = denied by condition
+  accept  message = accepted by condition
           condition = ${substr_5:$local_part}
 
 acl_56_56_58:
diff --git a/test/confs/0546 b/test/confs/0546
new file mode 100644 (file)
index 0000000..736c126
--- /dev/null
@@ -0,0 +1,46 @@
+# Exim test configuration 0546
+
+HELO_MSG=One line
+RCPT_MSG=RCPT is OK
+
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+rfc1413_query_timeout = 0s
+spool_directory = DIR/spool
+log_file_path = DIR/spool/log/%slog
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+
+# ----- Main settings -----
+
+acl_smtp_helo = check_helo
+acl_smtp_mail = check_mail
+acl_smtp_rcpt = check_rcpt
+acl_smtp_data = check_data
+acl_smtp_predata = check_predata
+
+qualify_domain = test.ex
+queue_only
+
+
+# ----- ACLs -----
+
+begin acl
+
+check_helo:
+  accept     message = HELO_MSG
+
+check_mail:
+  accept     message = 299 MAIL is\nOK
+
+check_rcpt:
+  accept     message = RCPT_MSG
+
+check_data:
+  accept     message = 288 I like the data
+
+check_predata:
+  accept     message = 300 Funny, but OK code
+
+
+# End
diff --git a/test/log/0546 b/test/log/0546
new file mode 100644 (file)
index 0000000..f1b6d35
--- /dev/null
@@ -0,0 +1,3 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex U=CALLER P=local-esmtp S=sss
+1999-03-02 09:44:33 EHLO/HELO response must not contain newlines: message truncated: 250 Two\nlines
+1999-03-02 09:44:33 configured error code starts with incorrect digit (expected 2) in "450 Bad number"
diff --git a/test/paniclog/0546 b/test/paniclog/0546
new file mode 100644 (file)
index 0000000..e21dfb1
--- /dev/null
@@ -0,0 +1,2 @@
+1999-03-02 09:44:33 EHLO/HELO response must not contain newlines: message truncated: 250 Two\nlines
+1999-03-02 09:44:33 configured error code starts with incorrect digit (expected 2) in "450 Bad number"
index a169d15..aee2697 100755 (executable)
@@ -1,6 +1,6 @@
 #! /usr/bin/perl -w
 
-# $Cambridge: exim/test/runtest,v 1.18 2006/11/07 15:11:34 ph10 Exp $
+# $Cambridge: exim/test/runtest,v 1.19 2006/11/14 16:40:36 ph10 Exp $
 
 ###############################################################################
 # This is the controlling script for the "new" test suite for Exim. It should #
@@ -1514,7 +1514,7 @@ if (/^server\s+(.*)$/)
   # This gives the server time to get started; otherwise the next
   # process may not find it there when it expects it.
 
-  select(undef, undef, undef, 0.05);
+  select(undef, undef, undef, 0.5);
   return 3;
   }
 
diff --git a/test/scripts/0000-Basic/0546 b/test/scripts/0000-Basic/0546
new file mode 100644 (file)
index 0000000..1486d35
--- /dev/null
@@ -0,0 +1,19 @@
+# User messages for "accept" verbs
+need_ipv4
+#
+exim -bs
+ehlo a.b.c
+mail from:<>
+rcpt to:<userx@test.ex>
+data
+This is a test
+.
+quit
+****
+exim -bs -DHELO_MSG='Two\nlines' -DRCPT_MSG='450 Bad number'
+ehlo a.b.c
+mail from:<>
+rcpt to:<userx@test.ex>
+quit
+****
+no_msglog_check
diff --git a/test/stderr/0546 b/test/stderr/0546
new file mode 100644 (file)
index 0000000..e21dfb1
--- /dev/null
@@ -0,0 +1,2 @@
+1999-03-02 09:44:33 EHLO/HELO response must not contain newlines: message truncated: 250 Two\nlines
+1999-03-02 09:44:33 configured error code starts with incorrect digit (expected 2) in "450 Bad number"
index e7eb769..266184a 100644 (file)
 221 myhost.test.ex closing connection\r
 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
 250 OK\r
-250 Accepted\r
+250 accepted by condition\r
 354 Enter message, ending with "." on a line by itself\r
 250 OK id=10HmbK-0005vi-00\r
 250 OK\r
diff --git a/test/stdout/0546 b/test/stdout/0546
new file mode 100644 (file)
index 0000000..0865c5f
--- /dev/null
@@ -0,0 +1,20 @@
+220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-One line\r
+250-SIZE 52428800\r
+250-PIPELINING\r
+250 HELP\r
+299-MAIL is\r
+299 OK\r
+250 RCPT is OK\r
+300 Funny, but OK code\r
+288 I like the data\r
+221 the.local.host.name closing connection\r
+220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-Two\r
+250-SIZE 52428800\r
+250-PIPELINING\r
+250 HELP\r
+299-MAIL is\r
+299 OK\r
+250 Bad number\r
+221 the.local.host.name closing connection\r