mime: cast to unsigned char for array accesses
[mailx.git] / mime.c
blob2375914f5648545b6366e58c02a4699821533301
1 #include <ctype.h>
2 #include <string.h>
3 #include "mime.h"
5 static char *b64_ch =
6 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7 static int b64_val[256];
9 static void b64_init(void)
11 int i;
12 for (i = 0; i < 64; i++)
13 b64_val[(unsigned char) b64_ch[i]] = i;
16 static int b64_dec(char *d, char *s)
18 unsigned v = 0;
19 v |= b64_val[(unsigned char) s[0]];
20 v <<= 6;
21 v |= b64_val[(unsigned char) s[1]];
22 v <<= 6;
23 v |= b64_val[(unsigned char) s[2]];
24 v <<= 6;
25 v |= b64_val[(unsigned char) s[3]];
27 d[2] = v & 0xff;
28 v >>= 8;
29 d[1] = v & 0xff;
30 v >>= 8;
31 d[0] = v & 0xff;
32 return 3 - (s[1] == '=') - (s[2] == '=') - (s[3] == '=');
35 static char *dec_b64(char *dst, char *src, int len)
37 char *end = src + len;
38 if (!b64_val['B'])
39 b64_init();
40 while (src + 4 <= end) {
41 while (src < end && isspace(*src))
42 src++;
43 if (src < end) {
44 dst += b64_dec(dst, src);
45 src += 4;
48 return dst;
51 static int hexval(int c)
53 if (c >= '0' && c <= '9')
54 return c - '0';
55 if (c >= 'A' && c <= 'F')
56 return 10 + c - 'A';
57 if (c >= 'a' && c <= 'f')
58 return 10 + c - 'a';
59 return 0;
62 static char *dec_qp(char *d, char *s, int len)
64 static char t[1 << 10];
65 char *end = s + len;
66 int i;
67 s--;
68 while (++s < end) {
69 if (*s != '=') {
70 *d++ = *s;
71 continue;
73 if (s[1] == '\n') {
74 s++;
75 continue;
77 if (s[1] == '?') {
78 char *enc_qu = memchr(s + 2, '?', end - s - 4);
79 if (enc_qu) {
80 int b64 = tolower(enc_qu[1]) == 'b';
81 char *b = enc_qu + 3;
82 char *e = memchr(b, '?', end - b);
83 if (e && sizeof(t) > e - b) {
84 for (i = 0; i < e - b; i++)
85 t[i] = b[i] == '_' ? ' ' : b[i];
86 d = (b64 ? dec_b64 : dec_qp)(d, t, e - b);
87 s = e + 1;
88 continue;
92 *d++ = hexval(s[1]) << 4 | hexval(s[2]);
93 s += 2;
95 return d;
98 #define MAXPARTS (1 << 3)
99 #define BOUNDLEN (1 << 7)
101 #define TYPE_TXT 0x00
102 #define TYPE_MPART 0x01
103 #define TYPE_ETC 0x02
104 #define ENC_B8 0x00
105 #define ENC_QP 0x10
106 #define ENC_B64 0x20
108 struct mime {
109 int depth;
110 int part[MAXPARTS];
111 char bound[MAXPARTS][BOUNDLEN];
112 char *src;
113 char *dst;
114 char *end;
117 static void copy_till(struct mime *m, char *s)
119 int len = s - m->src;
120 memcpy(m->dst, m->src, len);
121 m->dst += len;
122 m->src += len;
125 static int starts(char *r, char *s)
127 while (*s)
128 if (tolower(*s++) != tolower(*r++))
129 return 0;
130 return 1;
133 static void read_boundary(struct mime *m, char *s)
135 char *bound = m->bound[m->depth];
136 char *e;
137 s = memchr(s, '=', m->end - s);
138 if (!s)
139 return;
140 s++;
141 if (*s == '"') {
142 s++;
143 e = memchr(s, '"', m->end - s);
144 } else {
145 e = s;
146 while (e < m->end && !isspace(*e))
147 e++;
149 if (!e)
150 return;
151 bound[0] = '-';
152 bound[1] = '-';
153 memcpy(bound + 2, s, e - s);
154 bound[e - s + 2] = '\0';
155 m->depth++;
158 static int read_hdrs(struct mime *m)
160 char *s = m->src;
161 char *e = m->end;
162 int type = 0;
163 while (s && s < e && *s != '\n') {
164 char *n = memchr(s, '\n', e - s);
165 while (n && n + 1 < e && n[1] != '\n' && isspace(n[1]))
166 n = memchr(n + 1, '\n', e - n - 1);
167 if (!n)
168 break;
169 n++;
170 copy_till(m, s);
171 if (starts(s, "Subject:") || starts(s, "From:")) {
172 m->dst = dec_qp(m->dst, m->src, n - s);
173 m->src = n;
175 if (starts(s, "Content-Type:")) {
176 char *t = strchr(s, ':') + 1;
177 while (isspace(*t) && t < e)
178 t++;
179 if (starts(t, "text"))
180 type |= TYPE_TXT;
181 if (starts(t, "multipart")) {
182 type |= TYPE_MPART;
183 read_boundary(m, t);
186 if (starts(s, "Content-Transfer-Encoding:")) {
187 char *t = strchr(s, ':') + 1;
188 while (isspace(*t) && t < e)
189 t++;
190 if (starts(t, "quoted-printable"))
191 type |= ENC_QP;
192 if (starts(t, "base64"))
193 type |= ENC_B64;
196 s = n;
198 copy_till(m, s ? s + 1 : e);
199 return type;
202 static int is_bound(struct mime *m, char *s)
204 return starts(s, m->bound[m->depth - 1]);
207 static void read_bound(struct mime *m)
209 char *s = m->src;
210 int len = strlen(m->bound[m->depth - 1]);
211 if (s[len] == '-' && s[len + 1] == '-')
212 m->depth--;
213 s = memchr(s, '\n', m->end - s);
214 s = s ? s + 1 : m->end;
215 copy_till(m, s);
218 static char *find_bound(struct mime *m)
220 char *s = m->src;
221 char *e = m->end;
222 while (s < e) {
223 if (is_bound(m, s))
224 return s;
225 if (!(s = memchr(s, '\n', e - s)))
226 break;
227 s++;
229 return e;
232 static void read_body(struct mime *m, int type)
234 char *end = m->depth ? find_bound(m) : m->end;
235 if (~type & TYPE_TXT) {
236 copy_till(m, end);
237 return;
239 if (type & ENC_QP) {
240 m->dst = dec_qp(m->dst, m->src, end - m->src);
241 m->src = end;
242 return;
244 if (type & ENC_B64) {
245 m->dst = dec_b64(m->dst, m->src, end - m->src);
246 m->src = end;
247 return;
249 copy_till(m, end);
252 int mime_decode(char *dst, char *src, int len)
254 struct mime m;
255 memset(&m, 0, sizeof(m));
256 m.src = src;
257 m.dst = dst;
258 m.end = src + len;
259 while ((m.depth && m.src < m.end) || m.src == src) {
260 int type = read_hdrs(&m);
261 read_body(&m, type);
262 if (m.depth)
263 read_bound(&m);
265 *m.dst = '\0';
266 return m.dst - dst;