about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2021-08-30 17:49:32 +0200
committerLeah Neukirchen <leah@vuxu.org>2021-08-30 18:12:30 +0200
commit41bd429452f45cb43110ec4699871a2cbd9c0a5e (patch)
tree49fa1e50cb888dceb7341f88d3d465caa7d42823
parent4be6e0ce91c0690d3cbf58bd7b76144e01fe2be7 (diff)
downloadmblaze-41bd429452f45cb43110ec4699871a2cbd9c0a5e.tar.gz
mblaze-41bd429452f45cb43110ec4699871a2cbd9c0a5e.tar.xz
mblaze-41bd429452f45cb43110ec4699871a2cbd9c0a5e.zip
blaze822: blaze822/blaze822_mem: detect line ending before scanning end of header
A mail using CRLF which contained (for some reason) a LFLF pair would
be misparsed as the header was read until the LFLF.

Instead, scan for the first LF, check if it's preceded by CR,
and then search for the proper header terminator only.

Closes #212.
-rw-r--r--blaze822.c43
-rwxr-xr-xt/1701-mshow-regress.t29
2 files changed, 60 insertions, 12 deletions
diff --git a/blaze822.c b/blaze822.c
index e55ae49..9262f6f 100644
--- a/blaze822.c
+++ b/blaze822.c
@@ -420,10 +420,19 @@ unfold_hdr(char *buf, char *end)
 	compress_hdr(l, end);
 }
 
+static int
+is_crlf(char *s, size_t len)
+{
+	char *firsteol = memchr(s, '\n', len);
+
+	return firsteol && firsteol > s && firsteol[-1] == '\r';
+}
+
 struct message *
 blaze822(char *file)
 {
 	int fd;
+	int crlf;
 	ssize_t rd;
 	char *buf;
 	ssize_t bufalloc;
@@ -466,15 +475,21 @@ blaze822(char *file)
 			close(fd);
 			return 0;
 		}
-
-		if ((end = mymemmem(buf-overlap+used, rd+overlap, "\n\n", 2))) {
-			end++;
-			break;
+		if (used == 0) {
+			crlf = is_crlf(buf, rd);
 		}
-		if ((end = mymemmem(buf-overlap+used, rd+overlap, "\r\n\r\n", 4))) {
-			end++;
-			end++;
-			break;
+
+		if (crlf) {
+			if ((end = mymemmem(buf-overlap+used, rd+overlap, "\r\n\r\n", 4))) {
+				end++;
+				end++;
+				break;
+			}
+		} else {
+			if ((end = mymemmem(buf-overlap+used, rd+overlap, "\n\n", 2))) {
+				end++;
+				break;
+			}
 		}
 
 		used += rd;
@@ -502,11 +517,15 @@ blaze822_mem(char *src, size_t len)
 	if (!mesg)
 		return 0;
 
-	if ((end = mymemmem(src, len, "\n\n", 2))) {
-		mesg->body = end+2;
-	} else if ((end = mymemmem(src, len, "\r\n\r\n", 4))) {
-		mesg->body = end+4;
+	if (is_crlf(src, len)) {
+		if ((end = mymemmem(src, len, "\r\n\r\n", 4)))
+			mesg->body = end+4;
 	} else {
+		if ((end = mymemmem(src, len, "\n\n", 2)))
+			mesg->body = end+2;
+	}
+
+	if (!end) {
 		end = src + len;
 		mesg->body = end;
 		mesg->bodyend = end;
diff --git a/t/1701-mshow-regress.t b/t/1701-mshow-regress.t
new file mode 100755
index 0000000..ffb676d
--- /dev/null
+++ b/t/1701-mshow-regress.t
@@ -0,0 +1,29 @@
+#!/bin/sh -e
+cd ${0%/*}
+. ./lib.sh
+plan 2
+
+# Mail with \n\n and \r\n\r\n
+cr=$(printf '\r')
+cat <<EOF >tmp
+Content-Type: multipart/form-data; boundary=------------------------55a586f81559face$cr
+$cr
+--------------------------55a586f81559face$cr
+Content-Disposition: form-data; name="a"; filename="foo"$cr
+Content-Type: application/octet-stream$cr
+$cr
+foo$cr
+
+previously there are two NL$cr
+$cr
+--------------------------55a586f81559face$cr
+Content-Disposition: form-data; name="a"; filename="bar"$cr
+Content-Type: application/octet-stream$cr
+$cr
+bar$cr
+$cr
+--------------------------55a586f81559face--$cr
+EOF
+
+check 'mail has 3 attachments' 'mshow -t ./tmp | wc -l | grep 4'
+check 'mail attachment foo has size 35' 'mshow -t ./tmp | grep size=35.*name=\"foo\"'