Do not log submission mode rewrites if they do not change the address.
[exim.git] / src / src / receive.c
index 98a728b2fcbe49422aaff04c70b5663ea210ff9a..4d97a19300721e207c1f8e4ecc376b18ee298844 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/receive.c,v 1.36 2007/04/13 15:13:47 ph10 Exp $ */
+/* $Cambridge: exim/src/src/receive.c,v 1.44 2008/01/22 19:23:46 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 #include "exim.h"
 
 
 #include "exim.h"
 
+#if (defined EXPERIMENTAL_DOMAINKEYS) && (defined EXPERIMENTAL_DKIM)
+
+#warning Chaining Domainkeys via DKIM receive functions
+#define RECEIVE_GETC dkim_receive_getc
+#define RECEIVE_UNGETC dkim_receive_ungetc
+
+#else
+
+#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
+
 #ifdef EXPERIMENTAL_DOMAINKEYS
 #ifdef EXPERIMENTAL_DOMAINKEYS
+#warning Using Domainkeys receive functions
 #define RECEIVE_GETC dk_receive_getc
 #define RECEIVE_UNGETC dk_receive_ungetc
 #define RECEIVE_GETC dk_receive_getc
 #define RECEIVE_UNGETC dk_receive_ungetc
+#endif
+#ifdef EXPERIMENTAL_DKIM
+#warning Using DKIM receive functions
+#define RECEIVE_GETC dkim_receive_getc
+#define RECEIVE_UNGETC dkim_receive_ungetc
+#endif
+
 #else
 #else
+
+/* Normal operation */
 #define RECEIVE_GETC receive_getc
 #define RECEIVE_UNGETC receive_ungetc
 #define RECEIVE_GETC receive_getc
 #define RECEIVE_UNGETC receive_ungetc
+
+#endif
+
+#endif
+
+
+#ifdef EXPERIMENTAL_DCC
+extern int dcc_ok;
 #endif
 
 /*************************************************
 #endif
 
 /*************************************************
@@ -178,7 +206,7 @@ else
     }
   }
 
     }
   }
 
-/* We now have the patch; do the business */
+/* We now have the path; do the business */
 
 memset(&statbuf, 0, sizeof(statbuf));
 
 
 memset(&statbuf, 0, sizeof(statbuf));
 
@@ -283,12 +311,14 @@ that case is done by setting a flag to cause the log functions to call this
 function if there is an ultimate disaster. That is why it is globally
 accessible.
 
 function if there is an ultimate disaster. That is why it is globally
 accessible.
 
-Arguments:   SMTP response to give if in an SMTP session
+Arguments:
+  reason     text reason to pass to the not-quit ACL
+  msg        default SMTP response to give if in an SMTP session
 Returns:     it doesn't
 */
 
 void
 Returns:     it doesn't
 */
 
 void
-receive_bomb_out(uschar *msg)
+receive_bomb_out(uschar *reason, uschar *msg)
 {
 /* If spool_name is set, it contains the name of the data file that is being
 written. Unlink it before closing so that it cannot be picked up by a delivery
 {
 /* If spool_name is set, it contains the name of the data file that is being
 written. Unlink it before closing so that it cannot be picked up by a delivery
@@ -306,20 +336,16 @@ if (spool_name[0] != 0)
 if (data_file != NULL) (void)fclose(data_file);
   else if (data_fd >= 0) (void)close(data_fd);
 
 if (data_file != NULL) (void)fclose(data_file);
   else if (data_fd >= 0) (void)close(data_fd);
 
-/* Attempt to close down an SMTP connection tidily. */
+/* Attempt to close down an SMTP connection tidily. For non-batched SMTP, call
+smtp_notquit_exit(), which runs the NOTQUIT ACL, if present, and handles the
+SMTP response. */
 
 if (smtp_input)
   {
 
 if (smtp_input)
   {
-  if (!smtp_batched_input)
-    {
-    smtp_printf("421 %s %s - closing connection.\r\n", smtp_active_hostname,
-      msg);
-    mac_smtp_fflush();
-    }
-
-  /* Control does not return from moan_smtp_batch(). */
-
-  else moan_smtp_batch(NULL, "421 %s - message abandoned", msg);
+  if (smtp_batched_input)
+    moan_smtp_batch(NULL, "421 %s - message abandoned", msg);  /* No return */
+  smtp_notquit_exit(reason, US"421", US"%s %s - closing connection.",
+    smtp_active_hostname, msg);
   }
 
 /* Exit from the program (non-BSMTP cases) */
   }
 
 /* Exit from the program (non-BSMTP cases) */
@@ -362,7 +388,7 @@ else
             LOG_MAIN, "timed out while reading local message");
   }
 
             LOG_MAIN, "timed out while reading local message");
   }
 
-receive_bomb_out(msg);   /* Does not return */
+receive_bomb_out(US"data-timeout", msg);   /* Does not return */
 }
 
 
 }
 
 
@@ -384,7 +410,8 @@ local_scan_timeout_handler(int sig)
 sig = sig;    /* Keep picky compilers happy */
 log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function timed out - "
   "message temporarily rejected (size %d)", message_size);
 sig = sig;    /* Keep picky compilers happy */
 log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function timed out - "
   "message temporarily rejected (size %d)", message_size);
-receive_bomb_out(US"local verification problem");   /* Does not return */
+/* Does not return */
+receive_bomb_out(US"local-scan-timeout", US"local verification problem");
 }
 
 
 }
 
 
@@ -405,7 +432,8 @@ local_scan_crash_handler(int sig)
 {
 log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function crashed with "
   "signal %d - message temporarily rejected (size %d)", sig, message_size);
 {
 log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function crashed with "
   "signal %d - message temporarily rejected (size %d)", sig, message_size);
-receive_bomb_out(US"local verification problem");   /* Does not return */
+/* Does not return */
+receive_bomb_out(US"local-scan-error", US"local verification problem");
 }
 
 
 }
 
 
@@ -442,7 +470,7 @@ else
     }
   }
 
     }
   }
 
-receive_bomb_out(msg);    /* Does not return */
+receive_bomb_out(US"signal-exit", msg);    /* Does not return */
 }
 
 
 }
 
 
@@ -564,6 +592,7 @@ read_message_data(FILE *fout)
 {
 int ch_state;
 register int ch;
 {
 int ch_state;
 register int ch;
+register int linelength = 0;
 
 /* Handle the case when only EOF terminates the message */
 
 
 /* Handle the case when only EOF terminates the message */
 
@@ -576,6 +605,9 @@ if (!dot_ends)
     if (ch == 0) body_zerocount++;
     if (last_ch == '\r' && ch != '\n')
       {
     if (ch == 0) body_zerocount++;
     if (last_ch == '\r' && ch != '\n')
       {
+      if (linelength > max_received_linelength)
+        max_received_linelength = linelength;
+      linelength = 0;
       if (fputc('\n', fout) == EOF) return END_WERROR;
       message_size++;
       body_linecount++;
       if (fputc('\n', fout) == EOF) return END_WERROR;
       message_size++;
       body_linecount++;
@@ -583,12 +615,21 @@ if (!dot_ends)
     if (ch == '\r') continue;
 
     if (fputc(ch, fout) == EOF) return END_WERROR;
     if (ch == '\r') continue;
 
     if (fputc(ch, fout) == EOF) return END_WERROR;
-    if (ch == '\n') body_linecount++;
+    if (ch == '\n')
+      {
+      if (linelength > max_received_linelength)
+        max_received_linelength = linelength;
+      linelength = 0;
+      body_linecount++;
+      }
+    else linelength++;
     if (++message_size > thismessage_size_limit) return END_SIZE;
     }
 
   if (last_ch != '\n')
     {
     if (++message_size > thismessage_size_limit) return END_SIZE;
     }
 
   if (last_ch != '\n')
     {
+    if (linelength > max_received_linelength)
+      max_received_linelength = linelength;
     if (fputc('\n', fout) == EOF) return END_WERROR;
     message_size++;
     body_linecount++;
     if (fputc('\n', fout) == EOF) return END_WERROR;
     message_size++;
     body_linecount++;
@@ -608,25 +649,37 @@ while ((ch = (RECEIVE_GETC)()) != EOF)
     {
     case 0:                         /* Normal state (previous char written) */
     if (ch == '\n')
     {
     case 0:                         /* Normal state (previous char written) */
     if (ch == '\n')
-      { body_linecount++; ch_state = 1; }
+      {
+      body_linecount++;
+      if (linelength > max_received_linelength)
+        max_received_linelength = linelength;
+      linelength = -1;
+      ch_state = 1;
+      }
     else if (ch == '\r')
       { ch_state = 2; continue; }
     break;
 
     case 1:                         /* After written "\n" */
     if (ch == '.') { ch_state = 3; continue; }
     else if (ch == '\r')
       { ch_state = 2; continue; }
     break;
 
     case 1:                         /* After written "\n" */
     if (ch == '.') { ch_state = 3; continue; }
-    if (ch != '\n') ch_state = 0;
+    if (ch != '\n') ch_state = 0; else linelength = -1;
     break;
 
     case 2:
     body_linecount++;               /* After unwritten "\r" */
     break;
 
     case 2:
     body_linecount++;               /* After unwritten "\r" */
+    if (linelength > max_received_linelength)
+      max_received_linelength = linelength;
     if (ch == '\n')
     if (ch == '\n')
-      { ch_state = 1; }
+      {
+      ch_state = 1;
+      linelength = -1;
+      }
     else
       {
       if (message_size++, fputc('\n', fout) == EOF) return END_WERROR;
       if (ch == '\r') continue;
       ch_state = 0;
     else
       {
       if (message_size++, fputc('\n', fout) == EOF) return END_WERROR;
       if (ch == '\r') continue;
       ch_state = 0;
+      linelength = 0;
       }
     break;
 
       }
     break;
 
@@ -634,6 +687,7 @@ while ((ch = (RECEIVE_GETC)()) != EOF)
     if (ch == '\n') return END_DOT;
     if (ch == '\r') { ch_state = 4; continue; }
     message_size++;
     if (ch == '\n') return END_DOT;
     if (ch == '\r') { ch_state = 4; continue; }
     message_size++;
+    linelength++;
     if (fputc('.', fout) == EOF) return END_WERROR;
     ch_state = 0;
     break;
     if (fputc('.', fout) == EOF) return END_WERROR;
     ch_state = 0;
     break;
@@ -648,6 +702,7 @@ while ((ch = (RECEIVE_GETC)()) != EOF)
     break;
     }
 
     break;
     }
 
+  linelength++;
   if (fputc(ch, fout) == EOF) return END_WERROR;
   if (++message_size > thismessage_size_limit) return END_SIZE;
   }
   if (fputc(ch, fout) == EOF) return END_WERROR;
   if (++message_size > thismessage_size_limit) return END_SIZE;
   }
@@ -701,6 +756,7 @@ read_message_data_smtp(FILE *fout)
 {
 int ch_state = 0;
 register int ch;
 {
 int ch_state = 0;
 register int ch;
+register int linelength = 0;
 
 while ((ch = (RECEIVE_GETC)()) != EOF)
   {
 
 while ((ch = (RECEIVE_GETC)()) != EOF)
   {
@@ -722,6 +778,9 @@ while ((ch = (RECEIVE_GETC)()) != EOF)
       {
       ch_state = 0;
       body_linecount++;
       {
       ch_state = 0;
       body_linecount++;
+      if (linelength > max_received_linelength)
+        max_received_linelength = linelength;
+      linelength = -1;
       }
     else if (ch == '\r')
       {
       }
     else if (ch == '\r')
       {
@@ -732,6 +791,9 @@ while ((ch = (RECEIVE_GETC)()) != EOF)
 
     case 2:                             /* After (unwritten) CR */
     body_linecount++;
 
     case 2:                             /* After (unwritten) CR */
     body_linecount++;
+    if (linelength > max_received_linelength)
+      max_received_linelength = linelength;
+    linelength = -1;
     if (ch == '\n')
       {
       ch_state = 0;
     if (ch == '\n')
       {
       ch_state = 0;
@@ -773,6 +835,7 @@ while ((ch = (RECEIVE_GETC)()) != EOF)
   next. */
 
   message_size++;
   next. */
 
   message_size++;
+  linelength++;
   if (fout != NULL)
     {
     if (fputc(ch, fout) == EOF) return END_WERROR;
   if (fout != NULL)
     {
     if (fputc(ch, fout) == EOF) return END_WERROR;
@@ -1263,6 +1326,7 @@ int  header_size = 256;
 int  start, end, domain, size, sptr;
 int  id_resolution;
 int  had_zero = 0;
 int  start, end, domain, size, sptr;
 int  id_resolution;
 int  had_zero = 0;
+int  prevlines_length = 0;
 
 register int ptr = 0;
 
 
 register int ptr = 0;
 
@@ -1343,19 +1407,26 @@ data_fd = -1;
 spool_name[0] = 0;
 message_size = 0;
 warning_count = 0;
 spool_name[0] = 0;
 message_size = 0;
 warning_count = 0;
-received_count = 1;     /* For the one we will add */
+received_count = 1;            /* For the one we will add */
 
 if (thismessage_size_limit <= 0) thismessage_size_limit = INT_MAX;
 
 /* While reading the message, the following counts are computed. */
 
 
 if (thismessage_size_limit <= 0) thismessage_size_limit = INT_MAX;
 
 /* While reading the message, the following counts are computed. */
 
-message_linecount = body_linecount = body_zerocount = 0;
+message_linecount = body_linecount = body_zerocount =
+  max_received_linelength = 0;
 
 #ifdef EXPERIMENTAL_DOMAINKEYS
 /* Call into DK to set up the context. Check if DK is to be run are carried out
    inside dk_exim_verify_init(). */
 dk_exim_verify_init();
 #endif
 
 #ifdef EXPERIMENTAL_DOMAINKEYS
 /* Call into DK to set up the context. Check if DK is to be run are carried out
    inside dk_exim_verify_init(). */
 dk_exim_verify_init();
 #endif
+#ifdef EXPERIMENTAL_DKIM
+/* Call into DKIM to set up the context. Check if DKIM is to be run are carried out
+   inside dk_exim_verify_init(). */
+dkim_exim_verify_init();
+#endif
+
 
 /* Remember the time of reception. Exim uses time+pid for uniqueness of message
 ids, and fractions of a second are required. See the comments that precede the
 
 /* Remember the time of reception. Exim uses time+pid for uniqueness of message
 ids, and fractions of a second are required. See the comments that precede the
@@ -1585,6 +1656,12 @@ for (;;)
   receive_linecount++;
   message_linecount++;
 
   receive_linecount++;
   message_linecount++;
 
+  /* Keep track of maximum line length */
+
+  if (ptr - prevlines_length > max_received_linelength)
+    max_received_linelength = ptr - prevlines_length;
+  prevlines_length = ptr + 1;
+
   /* Now put in the terminating newline. There is always space for
   at least two more characters. */
 
   /* Now put in the terminating newline. There is always space for
   at least two more characters. */
 
@@ -1813,6 +1890,7 @@ for (;;)
   next->text = store_get(header_size);
   ptr = 0;
   had_zero = 0;
   next->text = store_get(header_size);
   ptr = 0;
   had_zero = 0;
+  prevlines_length = 0;
   }      /* Continue, starting to read the next header */
 
 /* At this point, we have read all the headers into a data structure in main
   }      /* Continue, starting to read the next header */
 
 /* At this point, we have read all the headers into a data structure in main
@@ -2507,9 +2585,10 @@ if (from_header != NULL &&
     if (sender_address_unrewritten == NULL)
       sender_address_unrewritten = sender_address;
     sender_address = generated_sender_address;
     if (sender_address_unrewritten == NULL)
       sender_address_unrewritten = sender_address;
     sender_address = generated_sender_address;
-    log_write(L_address_rewrite, LOG_MAIN,
-      "\"%s\" from env-from rewritten as \"%s\" by submission mode",
-      sender_address_unrewritten, generated_sender_address);
+    if (Ustrcmp(sender_address_unrewritten, generated_sender_address) != 0)
+      log_write(L_address_rewrite, LOG_MAIN,
+        "\"%s\" from env-from rewritten as \"%s\" by submission mode",
+        sender_address_unrewritten, generated_sender_address);
     }
   }
 
     }
   }
 
@@ -2931,6 +3010,9 @@ else
 #ifdef EXPERIMENTAL_DOMAINKEYS
     dk_exim_verify_finish();
 #endif
 #ifdef EXPERIMENTAL_DOMAINKEYS
     dk_exim_verify_finish();
 #endif
+#ifdef EXPERIMENTAL_DKIM
+    dkim_exim_verify_finish();
+#endif
 
 #ifdef WITH_CONTENT_SCAN
     if (acl_smtp_mime != NULL &&
 
 #ifdef WITH_CONTENT_SCAN
     if (acl_smtp_mime != NULL &&
@@ -3033,6 +3115,11 @@ else
 unspool_mbox();
 #endif
 
 unspool_mbox();
 #endif
 
+#ifdef EXPERIMENTAL_DCC
+dcc_ok = 0;
+#endif
+
+
 /* The final check on the message is to run the scan_local() function. The
 version supplied with Exim always accepts, but this is a hook for sysadmins to
 supply their own checking code. The local_scan() function is run even when all
 /* The final check on the message is to run the scan_local() function. The
 version supplied with Exim always accepts, but this is a hook for sysadmins to
 supply their own checking code. The local_scan() function is run even when all
@@ -3322,7 +3409,8 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
   s = string_append(s, &size, &sptr, 2, US" CV=",
     tls_certificate_verified? "yes":"no");
 if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
   s = string_append(s, &size, &sptr, 2, US" CV=",
     tls_certificate_verified? "yes":"no");
 if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
-  s = string_append(s, &size, &sptr, 3, US" DN=\"", tls_peerdn, US"\"");
+  s = string_append(s, &size, &sptr, 3, US" DN=\"",
+    string_printing(tls_peerdn), US"\"");
 #endif
 
 if (sender_host_authenticated != NULL)
 #endif
 
 if (sender_host_authenticated != NULL)
@@ -3436,21 +3524,26 @@ to cause a call to receive_bomb_out() if the log cannot be opened. */
 
 receive_call_bombout = TRUE;
 
 
 receive_call_bombout = TRUE;
 
-/* Before sending an SMTP response in a TCP/IP session, we check to see if
-there is unconsumed input (which there shouldn't be) or if the connection has
-gone away. This can be done because the end of a message is always a
-synchronization point. If the connection is still present, but there is no
-pending input, the result of a select() call will be zero. If, however, the
-connection has gone away, or if there is pending input, the result of select()
-will be non-zero. The two cases can be distinguished by trying to read the next
-input character. Of course, since TCP/IP is asynchronous, there is always a
-chance that the connection will vanish between the time of this test and the
-sending of the response, but the chance of this happening should be small.
+/* Before sending an SMTP response in a TCP/IP session, we check to see if the
+connection has gone away. This can only be done if there is no unconsumed input
+waiting in the local input buffer. We can test for this by calling
+receive_smtp_buffered(). RFC 2920 (pipelining) explicitly allows for additional
+input to be sent following the final dot, so the presence of following input is
+not an error.
 
 
-We also check for input that has already been received and is in the local
-input buffer (plain SMTP or TLS) by calling receive_smtp_buffered(). */
+If the connection is still present, but there is no unread input for the
+socket, the result of a select() call will be zero. If, however, the connection
+has gone away, or if there is pending input, the result of select() will be
+non-zero. The two cases can be distinguished by trying to read the next input
+character. If we succeed, we can unread it so that it remains in the local
+buffer for handling later. If not, the connection has been lost.
 
 
-if (smtp_input && sender_host_address != NULL && !sender_host_notsocket)
+Of course, since TCP/IP is asynchronous, there is always a chance that the
+connection will vanish between the time of this test and the sending of the
+response, but the chance of this happening should be small. */
+
+if (smtp_input && sender_host_address != NULL && !sender_host_notsocket &&
+    !receive_smtp_buffered())
   {
   struct timeval tv;
   fd_set select_check;
   {
   struct timeval tv;
   fd_set select_check;
@@ -3459,47 +3552,39 @@ if (smtp_input && sender_host_address != NULL && !sender_host_notsocket)
   tv.tv_sec = 0;
   tv.tv_usec = 0;
 
   tv.tv_sec = 0;
   tv.tv_usec = 0;
 
-  if (select(fileno(smtp_in) + 1, &select_check, NULL, NULL, &tv) != 0 ||
-      receive_smtp_buffered())
+  if (select(fileno(smtp_in) + 1, &select_check, NULL, NULL, &tv) != 0)
     {
     {
-    uschar *msg;
-    if ((RECEIVE_GETC)() == EOF)
-      {
-      msg = US"SMTP connection lost after final dot";
-      smtp_reply = US"";   /* No attempt to send a response */
-      }
-    else
+    int c = (RECEIVE_GETC)();
+    if (c != EOF) (RECEIVE_UNGETC)(c); else
       {
       {
-      msg = US"Synchronization error (data after final dot)";
-      smtp_reply = US"550 Synchronization error (data after final dot)";
-      }
-
-    /* Overwrite the log line workspace */
+      uschar *msg = US"SMTP connection lost after final dot";
+      smtp_reply = US"";    /* No attempt to send a response */
+      smtp_yield = FALSE;   /* Nothing more on this connection */
 
 
-    sptr = 0;
-    s = string_cat(s, &size, &sptr, msg, Ustrlen(msg));
-    s = add_host_info_for_log(s, &size, &sptr);
-    s[sptr] = 0;
-    log_write(0, LOG_MAIN, "%s", s);
+      /* Re-use the log line workspace */
 
 
-    /* We now have to delete the files for this aborted message. */
+      sptr = 0;
+      s = string_cat(s, &size, &sptr, msg, Ustrlen(msg));
+      s = add_host_info_for_log(s, &size, &sptr);
+      s[sptr] = 0;
+      log_write(0, LOG_MAIN, "%s", s);
 
 
-    sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory, message_subdir,
-      message_id);
-    Uunlink(spool_name);
+      /* Delete the files for this aborted message. */
 
 
-    sprintf(CS spool_name, "%s/input/%s/%s-H", spool_directory, message_subdir,
-      message_id);
-    Uunlink(spool_name);
+      sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory,
+        message_subdir, message_id);
+      Uunlink(spool_name);
 
 
-    sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory, message_subdir,
-      message_id);
-    Uunlink(spool_name);
+      sprintf(CS spool_name, "%s/input/%s/%s-H", spool_directory,
+        message_subdir, message_id);
+      Uunlink(spool_name);
 
 
-    /* Do not accept any more messages on this connection. */
+      sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory,
+        message_subdir, message_id);
+      Uunlink(spool_name);
 
 
-    smtp_yield = FALSE;
-    goto TIDYUP;
+      goto TIDYUP;
+      }
     }
   }
 
     }
   }