about summary refs log tree commit diff
path: root/mmime.c
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2016-07-25 15:19:19 +0200
committerChristian Neukirchen <chneukirchen@gmail.com>2016-07-25 15:19:19 +0200
commitb8c2685a601d09455064b7116d85fb2fd0a0f860 (patch)
tree63a0871f9a9867f2b2ff4db37e02477dcae04883 /mmime.c
parent70df63df73b2b337cb4b90e9a4b03c7b399c406a (diff)
downloadmblaze-b8c2685a601d09455064b7116d85fb2fd0a0f860.tar.gz
mblaze-b8c2685a601d09455064b7116d85fb2fd0a0f860.tar.xz
mblaze-b8c2685a601d09455064b7116d85fb2fd0a0f860.zip
mmime: encode headers when neccessary
This is still a bit clumsy, but I'm not sure how to make it better.
Diffstat (limited to 'mmime.c')
-rw-r--r--mmime.c107
1 files changed, 98 insertions, 9 deletions
diff --git a/mmime.c b/mmime.c
index 93377fc..2d240f3 100644
--- a/mmime.c
+++ b/mmime.c
@@ -54,7 +54,7 @@ int gen_b64(uint8_t *s, off_t size)
 	return 0;
 }
 
-int gen_qp(uint8_t *s, off_t size)
+int gen_qp(uint8_t *s, off_t size, int maxlinelen, int header)
 {
 	off_t i;
 	int linelen = 0;
@@ -67,8 +67,17 @@ int gen_qp(uint8_t *s, off_t size)
 			printf("=%02X", s[i]);
 			linelen += 3;
 			prev = s[i];
+		} else if (header &&
+			   (s[i] == '\n' || s[i] == '\t' || s[i] == '_')) {
+			printf("=%02X", s[i]);
+			linelen += 3;
+			prev = '_';
+		} else if (header && s[i] == ' ') {
+			putc_unlocked('_', stdout);
+			linelen++;
+			prev = '_';
 		} else if (s[i] == '\n') {
-			if (i > 1 && (prev == ' ' || prev == '\t'))
+			if (prev == ' ' || prev == '\t')
 				puts("=");
 			putc_unlocked('\n', stdout);
 			linelen = 0;
@@ -79,15 +88,15 @@ int gen_qp(uint8_t *s, off_t size)
 			prev = s[i];
 		}
 		
-		if (linelen >= 75) {
+		if (linelen >= maxlinelen-3) {
 			linelen = 0;
 			prev = '\n';
 			puts("=");
 		}
 	}
-	if (linelen > 0)
+	if (linelen > 0 && !header)
 		puts("=");
-	return 0;
+	return linelen;
 }
 
 static const char *
@@ -146,7 +155,8 @@ int gen_file(char *file, char *ct)
 			ct = "text/plain";
 		printf("Content-Type: %s\n", ct);
 		printf("Content-Transfer-Encoding: quoted-printable\n\n");
-		return gen_qp(content, st.st_size);
+		gen_qp(content, st.st_size, 78, 0);
+		return 0;
 	} else if (bitlow > st.st_size/10 || bithigh > st.st_size/4) {
 		if (!ct)
 			ct = "application/binary";
@@ -158,7 +168,8 @@ int gen_file(char *file, char *ct)
 			ct = "text/plain";
 		printf("Content-Type: %s\n", ct);
 		printf("Content-Transfer-Encoding: quoted-printable\n\n");
-		return gen_qp(content, st.st_size);
+		gen_qp(content, st.st_size, 78, 0);
+		return 0;
 	}
 }
 
@@ -182,6 +193,84 @@ gen_mixed(int argc, char *argv[])
 	return 0;
 }
 
+void
+print_header(char *line) {
+	char *s, *e;
+	size_t l = strlen(line);
+
+	if (line[l-1] == '\n')
+		line[l-1] = 0;
+
+	/* iterate word-wise, encode words when needed. */
+
+	s = line;
+	e = s;
+
+	if (!(*s == ' ' || *s == '\t')) {
+		// raw header name
+		while (*s && *s != ':')
+			putc_unlocked(*s++, stdout);
+		if (*s == ':')
+			putc_unlocked(*s++, stdout);
+	}
+
+	int prevq = 0;
+
+	int linelen = s - line;
+
+	while (*s) {
+		size_t highbit = 0;
+		e = s;
+		while (*e && *e == ' ')
+			e++;
+		for (; *e && *e != ' '; e++) {
+			if ((uint8_t) *e >= 127)
+				highbit++;
+		}
+		// use qp for lines with high bit, for long lines, or
+		// for more than two spaces
+		if (highbit ||
+		    e-s > 78-linelen ||
+		    (prevq && s[0] == ' ' && s[1] == ' ')) {
+			if (!prevq && s[0] == ' ')
+				s++;
+
+			// 13 = strlen(" =?UTF-8?Q??=")
+			int w;
+			while (s < e && (w = (78-linelen-13) / (highbit?3:1)) < e-s && w>0) {
+				printf(" =?UTF-8?Q?");
+				gen_qp((uint8_t *)s, w>e-s?e-s:w, 999, 1);
+				printf("?=\n");
+				s += w;
+				linelen = 0;
+			}
+			if (s < e) {
+				if (linelen + (e-s)+13 > 78) {
+					printf("\n");
+					linelen = 0;
+				}
+				printf(" =?UTF-8?Q?");
+				linelen += 13;
+				linelen += gen_qp((uint8_t *)s, e-s, 999, 1);
+				printf("?=");
+			}
+			prevq = 1;
+		} else {
+			if (linelen + (e-s) > 78) {
+				printf("\n");
+				if (*s != ' ')
+					printf(" ");
+				linelen = 0;
+			}
+			fwrite(s, 1, e-s, stdout);
+			linelen += e-s;
+			prevq = 0;
+		}
+		s = e;
+	}
+	printf("\n");
+}
+
 int
 gen_build()
 {
@@ -210,7 +299,7 @@ gen_build()
 				printf("\n");
 				printf("This is a multipart message in MIME format.\n\n");
 			} else {
-				printf("%s", line);
+				print_header(line);
 			}
 			continue;
 		}
@@ -237,7 +326,7 @@ gen_build()
 			intext = 1;
 		}
 
-		gen_qp((uint8_t *)line, strlen(line));
+		gen_qp((uint8_t *)line, strlen(line), 78, 0);
 	}
 	printf("--%s--\n", sep);