]> granicus.if.org Git - curl/commitdiff
(SMTP) support DATA better in the server and make sure to "escape" CRLF.CRLF
authorDaniel Stenberg <daniel@haxx.se>
Wed, 30 Dec 2009 21:52:27 +0000 (21:52 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 30 Dec 2009 21:52:27 +0000 (21:52 +0000)
sequences in uploaded data. The test server doesn't "decode" escaped dot-lines
but instead test cases must be written to take them into account. Added test
case 803 to verify dot-escaping.

lib/smtp.c
lib/smtp.h
lib/transfer.c
tests/data/Makefile.am
tests/data/test802
tests/data/test803 [new file with mode: 0644]
tests/ftpserver.pl

index 1bd869eb0cf84c969daa3da8d9dd39c6aadb0cb8..3dc50bd6a8f16cc9edab08fd77caffc1f1f21dfc 100644 (file)
@@ -639,6 +639,7 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
   struct SessionHandle *data = conn->data;
   struct FTP *smtp = data->state.proto.smtp;
   CURLcode result=CURLE_OK;
+  ssize_t bytes_written;
   (void)premature;
 
   if(!smtp)
@@ -653,6 +654,15 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
     conn->bits.close = TRUE; /* marked for closure */
     result = status;      /* use the already set error code */
   }
+  else
+    /* TODO: make this work even when the socket is EWOULDBLOCK in this call! */
+
+    /* write to socket (send away data) */
+    result = Curl_write(conn,
+                        conn->writesockfd,  /* socket to send to */
+                        SMTP_EOB,           /* buffer pointer */
+                        SMTP_EOB_LEN,       /* buffer size */
+                        &bytes_written);    /* actually sent away */
 
   /* clear these for next connection */
   smtp->transfer = FTPTRANSFER_BODY;
index 199481a0bfc0fcc118e71e51d44bee01c172c958..ec0bcfb8bab5111495933697f7bbc737113aa1fe 100644 (file)
@@ -58,4 +58,8 @@ extern const struct Curl_handler Curl_handler_smtps;
 #define SMTP_EOB "\x0d\x0a\x2e\x0d\x0a"
 #define SMTP_EOB_LEN 5
 
+/* if found in data, replace it with this string instead */
+#define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e"
+#define SMTP_EOB_REPL_LEN 4
+
 #endif /* __SMTP_H */
index 369481b18b3bbae14fd72a10d4262ff85d876ab3..ed9e349101724e5920e33e511c6708c2d2985bed 100644 (file)
@@ -784,6 +784,68 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
       /* store number of bytes available for upload */
       data->req.upload_present = nread;
 
+#ifndef CURL_DISABLE_SMTP
+      if(conn->protocol & PROT_SMTP) {
+        /* When sending SMTP payload, we must detect CRLF.CRLF sequences in
+         * the data and make sure it is sent as CRLF..CRLF instead, as
+         * otherwise it will wrongly be detected as end of data by the server.
+         */
+        struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+        if(data->state.scratch == NULL)
+          data->state.scratch = malloc(2*BUFSIZE);
+        if(data->state.scratch == NULL) {
+          failf (data, "Failed to alloc scratch buffer!");
+          return CURLE_OUT_OF_MEMORY;
+        }
+        /* This loop can be improved by some kind of Boyer-Moore style of
+           approach but that is saved for later... */
+        for(i = 0, si = 0; i < nread; i++, si++) {
+          int left = nread - i;
+
+          if(left>= (SMTP_EOB_LEN-smtpc->eob)) {
+            if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
+                       SMTP_EOB_LEN-smtpc->eob)) {
+              /* It matched, copy the replacement data to the target buffer
+                 instead. Note that the replacement does not contain the
+                 trailing CRLF but we instead continue to match on that one
+                 to deal with repeated sequences. Like CRLF.CRLF.CRLF etc
+              */
+              memcpy(&data->state.scratch[si], SMTP_EOB_REPL,
+                     SMTP_EOB_REPL_LEN);
+              si+=SMTP_EOB_REPL_LEN-1; /* minus one since the for() increments
+                                          it */
+              i+=SMTP_EOB_LEN-smtpc->eob-1-2;
+              smtpc->eob = 0; /* start over */
+              continue;
+            }
+          }
+          else if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
+                          left)) {
+            /* the last piece of the data matches the EOB so we can't send that
+               until we know the rest of it */
+            smtpc->eob += left;
+            break;
+          }
+
+          data->state.scratch[si] = data->req.upload_fromhere[i];
+        } /* for() */
+
+        if(si != nread) {
+          /* only use the new buffer if we replaced something */
+          nread = si;
+
+          /* upload from the new (replaced) buffer instead */
+          data->req.upload_fromhere = data->state.scratch;
+
+          /* set the new amount too */
+          data->req.upload_present = nread;
+        }
+
+      }
+      else
+#endif /* CURL_DISABLE_SMTP */
+
       /* convert LF to CRLF if so asked */
       if((!sending_http_headers) &&
 #ifdef CURL_DO_LINEEND_CONV
@@ -837,10 +899,10 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
 
     /* write to socket (send away data) */
     result = Curl_write(conn,
-                        conn->writesockfd,     /* socket to send to */
+                        conn->writesockfd,         /* socket to send to */
                         data->req.upload_fromhere, /* buffer pointer */
                         data->req.upload_present,  /* buffer size */
-                        &bytes_written);       /* actually send away */
+                        &bytes_written);           /* actually sent */
 
     if(result)
       return result;
index 3094d642a12089736555e945bdc782e7bb7477e5..b2c4cc9b5d26cb7202cffef810690de3fe0533ab 100644 (file)
@@ -63,7 +63,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46           \
  test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096   \
  test1097 test560 test561 test1098 test1099 test562 test563 test1100       \
  test564 test1101 test1102 test1103 test1104 test299 test310 test311       \
- test312 test1105 test565 test800 test1106 test801 test566 test802
+ test312 test1105 test565 test800 test1106 test801 test566 test802 test803
 
 filecheck:
        @mkdir test-place; \
index 9c7013a5c116c009a81b82e7edc69835406462b7..59d2e8c1ff6c726322dddbd1b3a11da9ae424721 100644 (file)
@@ -38,11 +38,15 @@ EHLO user
 MAIL FROM:802@from\r
 RCPT TO:802@foo\r
 DATA\r
+QUIT\r
+</protocol>
+<upload>
 From: different
 To: another
 
 body
-QUIT\r
-</protocol>
+\r
+.\r
+</upload>
 </verify>
 </testcase>
diff --git a/tests/data/test803 b/tests/data/test803
new file mode 100644 (file)
index 0000000..e6e48b5
--- /dev/null
@@ -0,0 +1,64 @@
+<testcase>
+<info>
+<keywords>
+SMTP
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+smtp
+</server>
+ <name>
+SMTP with CRLF-dot-CRLF in data
+ </name>
+<stdin>
+From: different
+To: another
+
+\r
+.\r
+.\r
+\r
+.\r
+
+body
+</stdin>
+ <command>
+smtp://%HOSTIP:%SMTPPORT -u user:secret --mail-rcpt 803@foo --mail-from 803@from -T -
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+EHLO user\r
+MAIL FROM:803@from\r
+RCPT TO:803@foo\r
+DATA\r
+QUIT\r
+</protocol>
+<upload>
+From: different
+To: another
+
+\r
+..\r
+..\r
+\r
+..\r
+
+body
+\r
+.\r
+</upload>
+</verify>
+</testcase>
index ccdc4cbb42fc59cccc6fd6296487b4c3b192f588..91f5412356220631e1da26bc92e481318d663a97 100644 (file)
@@ -473,12 +473,69 @@ sub DATA_smtp {
 
     if($testno eq "verifiedserver") {
         sendcontrol "554 WE ROOLZ: $$\r\n";
+        return 0; # don't wait for data now
     }
     else {
+        $testno =~ s/^([0-9]*).*/$1/;
         sendcontrol "354 Show me the mail\r\n";
     }
 
     logmsg "===> rcpt $testno was $smtp_rcpt\n";
+
+    my $filename = "log/upload.$testno";
+
+    logmsg "Store test number $testno in $filename\n";
+
+    open(FILE, ">$filename") ||
+        return 0; # failed to open output
+
+    my $line;
+    my $ulsize=0;
+    my $disc=0;
+    my $raw;
+    while (5 == (sysread \*SFREAD, $line, 5)) {
+        if($line eq "DATA\n") {
+            my $i;
+            my $eob;
+            sysread \*SFREAD, $i, 5;
+
+            my $size = 0;
+            if($i =~ /^([0-9a-fA-F]{4})\n/) {
+                $size = hex($1);
+            }
+
+            sysread \*SFREAD, $line, $size;
+            
+            $ulsize += $size;
+            print FILE $line if(!$nosave);
+
+            $raw .= $line;
+            if($raw =~ /\x0d\x0a\x2e\x0d\x0a\z/) {
+                # end of data marker!
+                $eob = 1;
+            }
+            logmsg "> Appending $size bytes to file\n";
+            if($eob) {
+                logmsg "Found SMTP EOB marker\n";
+                last;
+            }
+        }
+        elsif($line eq "DISC\n") {
+            # disconnect!
+            $disc=1;
+            last;
+        }
+        else {
+            logmsg "No support for: $line";
+            last;
+        }
+    }
+    if($nosave) {
+        print FILE "$ulsize bytes would've been stored here\n";
+    }
+    close(FILE);
+    logmsg "received $ulsize bytes upload\n";
+
 }
 
 sub RCPT_smtp {