Update version number and copyright year.
[exim.git] / src / src / smtp_in.c
index 36c4c3021d921a7f8540ab3ded2f898d45d8bada..7e80c6209afd643bcae18a84c27a2b729ffea838 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.44 2006/09/25 10:14:20 ph10 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.49 2007/01/08 10:50:18 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 
 /*************************************************
 *     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. */
 
 /* Functions for handling an incoming SMTP call. */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for handling an incoming SMTP call. */
@@ -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
 
 /* 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)
   {
 
 while (acl_warn_logged != NULL)
   {
@@ -1141,7 +1141,9 @@ BOOL
 smtp_start_session(void)
 {
 int size = 256;
 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 */
 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 */
 
 
 /* Run the ACL if it exists */
 
+user_msg = NULL;
 if (acl_smtp_connect != NULL)
   {
   int rc;
 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)
   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. */
 
 /* 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 > 4)
+    {
+    esc = code + 4;
+    esclen = codelen - 4;
+    }
+  }
 
 /* Remove any terminating newlines; might as well remove trailing space too */
 
 
 /* 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');
   {
   int len;
   uschar *linebreak = Ustrchr(p, '\n');
+  ss = string_cat(ss, &size, &ptr, code, 3);
   if (linebreak == NULL)
     {
     len = Ustrlen(p);
   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;
     }
   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;
   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
 
 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 > 4 there's an ESC
   final         FALSE if the last line isn't the final line
   msg           message text, possibly containing newlines
 
   final         FALSE if the last line isn't the final line
   msg           message text, possibly containing newlines
 
@@ -1786,7 +1808,7 @@ uschar *esc = US"";
 
 if (!final && no_multiline_responses) return;
 
 
 if (!final && no_multiline_responses) return;
 
-if (codelen > 3)
+if (codelen > 4)
   {
   esc = code + 4;
   esclen = codelen - 4;
   {
   esc = code + 4;
   esclen = codelen - 4;
@@ -1818,6 +1840,65 @@ 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.
+
+Note that the code length returned includes the terminating whitespace
+character, which is always included in the regex match.
+
+Arguments:
+  code          SMTP code, may involve extended status codes
+  codelen       length of smtp code; if > 4 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                *
 *************************************************/
 /*************************************************
 *           Handle an ACL failure                *
 *************************************************/
@@ -1858,7 +1939,6 @@ smtp_handle_acl_fail(int where, int rc, uschar *user_msg, uschar *log_msg)
 {
 BOOL drop = rc == FAIL_DROP;
 int codelen = 3;
 {
 BOOL drop = rc == FAIL_DROP;
 int codelen = 3;
-int ovector[3];
 uschar *smtp_code;
 uschar *lognl;
 uschar *sender_info = US"";
 uschar *smtp_code;
 uschar *lognl;
 uschar *sender_info = US"";
@@ -1874,40 +1954,10 @@ uschar *what =
 
 if (drop) rc = FAIL;
 
 
 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];
 
 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
 
 /* 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
@@ -2054,6 +2104,16 @@ if (sender_helo_name == NULL)
   HDEBUG(D_receive) debug_printf("no EHLO/HELO command was issued\n");
   }
 
   HDEBUG(D_receive) debug_printf("no EHLO/HELO command was issued\n");
   }
 
+/* Deal with the case of -bs without an IP address */
+
+else if (sender_host_address == NULL)
+  {
+  HDEBUG(D_receive) debug_printf("no client IP address: assume success\n");
+  helo_verified = TRUE;
+  }
+
+/* Deal with the more common case when there is a sending IP address */
+
 else if (sender_helo_name[0] == '[')
   {
   helo_verified = Ustrncmp(sender_helo_name+1, sender_host_address,
 else if (sender_helo_name[0] == '[')
   {
   helo_verified = Ustrncmp(sender_helo_name+1, sender_host_address,
@@ -2119,7 +2179,7 @@ else
     h.next = NULL;
     HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
       sender_helo_name);
     h.next = NULL;
     HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
       sender_helo_name);
-    rc = host_find_byname(&h, NULL, NULL, TRUE);
+    rc = host_find_byname(&h, NULL, 0, NULL, TRUE);
     if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
       {
       host_item *hh = &h;
     if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
       {
       host_item *hh = &h;
@@ -2139,13 +2199,40 @@ else
     }
   }
 
     }
   }
 
-if (!helo_verified) helo_verify_failed = FALSE;  /* We've tried ... */
+if (!helo_verified) helo_verify_failed = TRUE;  /* We've tried ... */
 return yield;
 }
 
 
 
 
 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     *
 *************************************************/
 /*************************************************
 *       Initialize for SMTP incoming message     *
 *************************************************/
@@ -2216,7 +2303,8 @@ while (done <= 0)
   uschar *etrn_command;
   uschar *etrn_serialize_key;
   uschar *errmess;
   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;
   uschar *recipient = NULL;
   uschar *hello = NULL;
   uschar *set_id = NULL;
@@ -2553,26 +2641,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;
 
     auth_advertised = FALSE;
     pipelining_advertised = FALSE;
@@ -2580,21 +2653,46 @@ while (done <= 0)
     tls_advertised = FALSE;
     #endif
 
     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 plus space*/
+    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)
+      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. Note
+    that the code returned by smtp_message_code() includes the terminating
+    whitespace character. */
+
+    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 = 4;
+      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);
       }
 
     s = string_cat(s, &size, &ptr, US"\r\n", 2);
@@ -2614,12 +2712,14 @@ while (done <= 0)
 
       if (thismessage_size_limit > 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, 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
         }
 
       /* Exim does not do protocol conversion or data conversion. It is 8-bit
@@ -2630,14 +2730,18 @@ while (done <= 0)
       provided as an option. */
 
       if (accept_8bitmime)
       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)
         {
 
       /* 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
         }
 
       /* Advertise EXPN if there's an ACL checking whether a host is
@@ -2645,7 +2749,8 @@ while (done <= 0)
 
       if (acl_smtp_expn != NULL)
         {
 
       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
         }
 
       /* Exim is quite happy with pipelining, so let the other end know that
@@ -2653,7 +2758,8 @@ while (done <= 0)
 
       if (verify_check_host(&pipelining_advertise_hosts) == OK)
         {
 
       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;
         }
         sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
         pipelining_advertised = TRUE;
         }
@@ -2683,7 +2789,8 @@ while (done <= 0)
               int saveptr;
               if (first)
                 {
               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;
                 }
                 first = FALSE;
                 auth_advertised = TRUE;
                 }
@@ -2709,14 +2816,16 @@ while (done <= 0)
       if (tls_active < 0 &&
           verify_check_host(&tls_advertise_hosts) != FAIL)
         {
       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. */
 
         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
       }
 
     /* Terminate the string (for debug), write it, and note that HELO/EHLO
@@ -2737,6 +2846,20 @@ while (done <= 0)
       debug_printf("SMTP>> %s", s);
       }
     helo_seen = TRUE;
       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 */
 
 
     break;   /* HELO/EHLO */
 
 
@@ -3014,12 +3137,12 @@ while (done <= 0)
 
     if (rc == OK || rc == DISCARD)
       {
 
     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;
       }
       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);
     else
       {
       done = smtp_handle_acl_fail(ACL_WHERE_MAIL, rc, user_msg, log_msg);
@@ -3174,7 +3297,8 @@ while (done <= 0)
 
     if (rc == OK)
       {
 
     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);
       }
 
       receive_add_recipient(recipient, -1);
       }
 
@@ -3182,7 +3306,8 @@ while (done <= 0)
 
     else if (rc == DISCARD)
       {
 
     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: "
       rcpt_fail_count++;
       discarded = TRUE;
       log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> rejected RCPT %s: "
@@ -3249,7 +3374,9 @@ while (done <= 0)
 
     if (rc == OK)
       {
 
     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 */
       }
       done = 3;
       message_ended = END_NOTENDED;   /* Indicate in middle of data */
       }
@@ -3451,12 +3578,11 @@ while (done <= 0)
         log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
           log_msg);
       }
         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
 
     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);
 
     #ifdef SUPPORT_TLS
     tls_close(TRUE);
@@ -3596,7 +3722,8 @@ while (done <= 0)
         debug_printf("ETRN command is: %s\n", etrn_command);
         debug_printf("ETRN command execution skipped\n");
         }
         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;
       }
 
       break;
       }
 
@@ -3672,7 +3799,11 @@ while (done <= 0)
       smtp_printf("458 Unable to fork process\r\n");
       if (smtp_etrn_serialize) enq_end(etrn_serialize_key);
       }
       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;
 
     signal(SIGCHLD, oldsignal);
     break;