]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
smtp: fix EOB handling
authorStefan Eissing <stefan@eissing.org>
Wed, 22 Oct 2025 13:04:53 +0000 (15:04 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 23 Oct 2025 11:03:10 +0000 (13:03 +0200)
SMTP automatically appends a \n.\n to an upload if there is not already
one at the end of the input. The implementation had a bug where this did
not happen, depending on read size and buffering.

Change test 900 to reproduce the failure. The bug only happened for mail
body input of known length, where EOS was known on the last chunk read.
Change test 900 to use an input file and make it large enough.

Fixes #18798
Closes #19193
Reported-by: madoe on github
lib/smtp.c
tests/data/test900

index 30f85357657e3b0e7f198566c357fd1b4eeded6f..3d4f36364bfaac791574ec5ed6f7143499773caf 100644 (file)
@@ -1927,6 +1927,7 @@ struct cr_eob_ctx {
   size_t eob;       /* Number of bytes of the EOB (End Of Body) that
                        have been received so far */
   BIT(read_eos);  /* we read an EOS from the next reader */
+  BIT(processed_eos);  /* we read and processed an EOS */
   BIT(eos);       /* we have returned an EOS */
 };
 
@@ -1967,6 +1968,8 @@ static CURLcode cr_eob_read(struct Curl_easy *data,
   if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
     /* Get more and convert it when needed */
     result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
+    CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d",
+                  blen, result, nread, eos);
     if(result)
       return result;
 
@@ -2010,31 +2013,34 @@ static CURLcode cr_eob_read(struct Curl_easy *data,
           return result;
       }
     }
+  }
 
-    if(ctx->read_eos) {
-      /* if we last matched a CRLF or if the data was empty, add ".\r\n"
-       * to end the body. If we sent something and it did not end with "\r\n",
-       * add "\r\n.\r\n" to end the body */
-      const char *eob = SMTP_EOB;
-      switch(ctx->n_eob) {
-        case 2:
-          /* seen a CRLF at the end, just add the remainder */
-          eob = &SMTP_EOB[2];
-          break;
-        case 3:
-          /* ended with '\r\n.', we should escape the last '.' */
-          eob = "." SMTP_EOB;
-          break;
-        default:
-          break;
-      }
-      result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
-      if(result)
-        return result;
+  *peos = FALSE;
+
+  if(ctx->read_eos && !ctx->processed_eos) {
+    /* if we last matched a CRLF or if the data was empty, add ".\r\n"
+     * to end the body. If we sent something and it did not end with "\r\n",
+     * add "\r\n.\r\n" to end the body */
+    const char *eob = SMTP_EOB;
+    CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'");
+    switch(ctx->n_eob) {
+      case 2:
+        /* seen a CRLF at the end, just add the remainder */
+        eob = &SMTP_EOB[2];
+        break;
+      case 3:
+        /* ended with '\r\n.', we should escape the last '.' */
+        eob = "." SMTP_EOB;
+        break;
+      default:
+        break;
     }
+    result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
+    if(result)
+      return result;
+    ctx->processed_eos = TRUE;
   }
 
-  *peos = FALSE;
   if(!Curl_bufq_is_empty(&ctx->buf)) {
     result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
   }
@@ -2043,6 +2049,7 @@ static CURLcode cr_eob_read(struct Curl_easy *data,
 
   if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
     /* no more data, read all, done. */
+    CURL_TRC_SMTP(data, "mail body complete, returning EOS");
     ctx->eos = TRUE;
   }
   *peos = ctx->eos;
index 371544b7657adb60c3799e3cd34871e2d7751dfe..f3aae4b6310ff157332ac830218aa86c49739eb8 100644 (file)
@@ -19,14 +19,16 @@ smtp
 <name>
 SMTP
 </name>
-<stdin>
+<file name="%LOGDIR/mail%TESTNUMBER">
 From: different\r
 To: another\r
 \r
 body\r
-</stdin>
+%repeat[6553 x 0123456789]%
+</file>
+
 <command>
-smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com --mail-from sender@example.com -T -
+smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com --mail-from sender@example.com -T %LOGDIR/mail%TESTNUMBER
 </command>
 </client>
 
@@ -45,6 +47,8 @@ From: different
 To: another\r
 \r
 body\r
+%repeat[6553 x 0123456789]%
+\r
 .\r
 </upload>
 </verify>