summary refs log tree commit diff
path: root/rfc2231.c
diff options
context:
space:
mode:
Diffstat (limited to 'rfc2231.c')
-rw-r--r--rfc2231.c127
1 files changed, 127 insertions, 0 deletions
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;
+}