summary refs log tree commit diff
path: root/rfc2231.c
blob: c0edaff7fcd21b0615fb95e3b635fa1e86064e58 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#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);
	if (blaze822_mime_parameter(s, namenum, &sbuf, &ebuf)) {
		i = 100;
		goto found_extended;
	}
	if (blaze822_mime_parameter(s, name, &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 || i == 100) { // 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;   // strip last *
			if (blaze822_mime_parameter(s, namenum, &sbuf, &ebuf)) {
found_plain:
				// copy plain
				if (ebuf - sbuf < dstend - dst) {
					memcpy(dst, sbuf, ebuf - sbuf);
					dst += ebuf - sbuf;
				}
				*dst = 0;
			} 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;
}