summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2017-04-06 20:59:41 +0200
committerLeah Neukirchen <leah@vuxu.org>2017-04-06 21:02:16 +0200
commitc6480f4785987aa97db25e7baa5adb394f8007a8 (patch)
treea5df0ae828dbfcad9ce99b2d14d9f6db586758a3
parentda907f0045cc26c2c7b066ddaa1b1df462dab5ed (diff)
downloadmblaze-c6480f4785987aa97db25e7baa5adb394f8007a8.tar.gz
mblaze-c6480f4785987aa97db25e7baa5adb394f8007a8.tar.xz
mblaze-c6480f4785987aa97db25e7baa5adb394f8007a8.zip
rfc2231: add RFC 2231 mime parameter parsing
-rw-r--r--blaze822.h4
-rw-r--r--rfc2231.c127
2 files changed, 131 insertions, 0 deletions
diff --git a/blaze822.h b/blaze822.h
index e767114..4dfd8f5 100644
--- a/blaze822.h
+++ b/blaze822.h
@@ -45,6 +45,10 @@ typedef enum { MIME_CONTINUE, MIME_STOP, MIME_PRUNE } blaze822_mime_action;
 typedef blaze822_mime_action (*blaze822_mime_callback)(int, struct message *, char *, size_t);
 blaze822_mime_action blaze822_walk_mime(struct message *, int, blaze822_mime_callback);
 
+// rfc2231.c
+
+int blaze822_mime2231_parameter(char *, char *, char *, size_t, char *);
+
 // seq.c
 
 char *blaze822_seq_open(char *file);
diff --git a/rfc2231.c b/rfc2231.c
new file mode 100644
index 0000000..5a05f86
--- /dev/null
+++ b/rfc2231.c
@@ -0,0 +1,127 @@
+#include <iconv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "blaze822.h"
+
+int
+blaze822_mime2231_parameter(char *s, char *name,
+    char *dst, size_t dlen, char *tgtenc)
+{
+	static signed char hex[] = {
+		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		 0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,
+		-1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		-1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
+	};
+
+	int i = 0;
+	char namenum[64];
+
+	char *srcenc = 0;
+
+	char *dststart = dst;
+	char *dstend = dst + dlen;
+
+	char *sbuf, *ebuf;
+
+	snprintf(namenum, sizeof namenum, "%s*", name, i);
+	if (blaze822_mime_parameter(s, namenum, &sbuf, &ebuf)) {
+		i = 100;
+		goto found_extended;
+	}
+	if (blaze822_mime_parameter(s, namenum, &sbuf, &ebuf)) {
+		i = 100;
+		goto found_plain;
+	}
+	
+	while (i < 100) {
+		snprintf(namenum, sizeof namenum, "%s*%d*", name, i);
+		if (blaze822_mime_parameter(s, namenum, &sbuf, &ebuf)) {
+found_extended:
+			// decode extended
+			if (i == 0) { // extended-initial-value
+				char *encstart = sbuf;
+				sbuf = strchr(sbuf, '\'');
+				if (!sbuf) 
+					return 0;
+				srcenc = strndup(encstart, sbuf - encstart);
+				if (!srcenc)
+					return 0;
+				sbuf = strchr(sbuf+1, '\'');
+				if (!sbuf) 
+					return 0;
+				sbuf++;
+			}
+			while (sbuf < ebuf && dst < dstend) {
+				if (sbuf[0] == '%') {
+					unsigned char c1 = sbuf[1];
+					unsigned char c2 = sbuf[2];
+					if (c1 < 127 && c2 < 127 &&
+					    hex[c1] > -1 && hex[c2] > -1) {
+						*dst++ = (hex[c1] << 4) | hex[c2];
+						sbuf += 3;
+					} else {
+						*dst++ = *sbuf++;
+					}
+				} else {
+					*dst++ = *sbuf++;
+				}
+			}
+			*dst = 0;
+		} else {
+			namenum[strlen(namenum) - 1] = 0;
+			if (blaze822_mime_parameter(s, namenum, &sbuf, &ebuf)) {
+found_plain:
+				// copy plain
+				if (dstend - dst < ebuf - sbuf) {
+					memcpy(dst, sbuf, ebuf - sbuf);
+					dst += ebuf - sbuf;
+				}
+			} else {
+				break;
+			}
+		}
+		i++;
+	}
+
+	if (i <= 0)
+		return 0;
+
+	if (!srcenc)
+		return 1;
+
+	iconv_t ic = iconv_open(tgtenc, srcenc);
+	free(srcenc);
+	if (ic == (iconv_t)-1)
+		return 1;
+
+	size_t tmplen = dlen;
+	char *tmp = malloc(tmplen);
+	if (!tmp)
+		return 1;
+	char *tmpend = tmp;
+
+	size_t dstlen = dst - dststart;
+	dst = dststart;
+	
+	int r = iconv(ic, &dst, &dstlen, &tmpend, &tmplen);
+	if (r < 0) {
+		free(tmp);
+		return 1;
+	}
+
+	iconv_close(ic);
+
+	// copy back
+	memcpy(dststart, tmp, tmpend - tmp);
+	dststart[tmpend - tmp] = 0;
+	free(tmp);
+
+	return 1;
+}