Fix missing line termination on the last received BDAT chunk (Bug 1974)
authorHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
Tue, 14 Feb 2017 18:38:41 +0000 (19:38 +0100)
committerPhil Pennock <pdp@exim.org>
Tue, 14 Feb 2017 23:24:27 +0000 (18:24 -0500)
(cherry picked from commit d953610fa77ec9a08fad9c1a183181079a79674e)
Signed-off-by: Phil Pennock <pdp@exim.org>
doc/doc-txt/ChangeLog
src/src/receive.c

index 39e17b3509fc485bed763936f61bde19fa919d96..23f05ebe92bbb3a37fecf1e78c37a733391faa03 100644 (file)
@@ -102,6 +102,10 @@ JH/17 Fix inbound CHUNKING when DKIM disabled at runtime.
 HS/01 Fix portability problems introduced by PP/08 for platforms where
       realloc(NULL) is not equivalent to malloc() [SunOS et al].
 
+HS/02 Bug 1974: Fix missing line terminator on the last received BDAT
+      chunk. This allows us to accept broken chunked messages. We need a more
+      general solution here.
+
 
 Exim version 4.88
 -----------------
index b152ceefb68173f5b1c2e1f5d2e8f8f2ea874c28..5125a4f47525fbb6af01d8b0add9cd255daea2df 100644 (file)
@@ -916,14 +916,41 @@ read_message_bdat_smtp(FILE *fout)
 {
 int linelength = 0, ch;
 enum CH_STATE ch_state = LF_SEEN;
+BOOL fix_nl = FALSE;
 
 for(;;)
   {
   switch ((ch = (bdat_getc)(GETC_BUFFER_UNLIMITED)))
     {
     case EOF:  return END_EOF;
-    case EOD:  return END_DOT;         /* normal exit */
     case ERR:  return END_PROTOCOL;
+    case EOD:
+      /* Nothing to get from the sender anymore. We check the last
+      character written to the spool.
+
+      RFC 3030 states, that BDAT chunks are normal text, terminated by CRLF.
+      If we would be strict, we would refuse such broken messages.
+      But we are liberal, so we fix it.  It would be easy just to append
+      the "\n" to the spool.
+
+      But there are some more things (line counting, message size calculation and such),
+      that would need to be duplicated here.  So we simply do some ungetc
+      trickery.
+      */
+      fseek(fout, -1, SEEK_CUR);
+      if (fgetc(fout) == '\n') return END_DOT;
+
+      if (linelength == -1)    /* \r already seen (see below) */
+        {
+        DEBUG(D_receive) debug_printf("Add missing LF\n");
+        bdat_ungetc('\n');
+        continue;
+        }
+      DEBUG(D_receive) debug_printf("Add missing CRLF\n");
+      bdat_ungetc('\r');      /* not even \r was seen */
+      fix_nl = TRUE;
+
+      continue;
     case '\0':  body_zerocount++; break;
     }
   switch (ch_state)
@@ -944,6 +971,7 @@ for(;;)
       else if (ch == '\r')
        {
        ch_state = CR_SEEN;
+       if (fix_nl) bdat_ungetc('\n');
        continue;                       /* don't write CR */
        }
       break;