mime: ignore incomplete b64 statements
[mailx.git] / mime.c
blob39d13f20d8c19db469d4e1d16dee536abeecd358
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[b64_ch[i]] = i;
16 static int b64_dec(char *d, char *s)
18 unsigned v = 0;
19 v |= b64_val[s[0]];
20 v <<= 6;
21 v |= b64_val[s[1]];
22 v <<= 6;
23 v |= b64_val[s[2]];
24 v <<= 6;
25 v |= b64_val[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 char *end = s + len;
65 s--;
66 while (++s < end) {
67 if (*s != '=') {
68 *d++ = *s;
69 continue;
71 if (s[1] == '\n') {
72 s++;
73 continue;
75 if (s[1] == '?') {
76 char *enc_qu = memchr(s + 2, '?', end - s - 4);
77 if (enc_qu) {
78 int b64 = tolower(enc_qu[1]) == 'b';
79 char *b = enc_qu + 3;
80 char *e = memchr(b, '?', end - b);
81 if (e) {
82 d = (b64 ? dec_b64 : dec_qp)(d, b, e - b);
83 s = e + 1;
84 continue;
88 *d++ = hexval(s[1]) << 4 | hexval(s[2]);
89 s += 2;
91 return d;
94 #define MAXPARTS (1 << 3)
95 #define BOUNDLEN (1 << 7)
97 #define TYPE_TXT 0x00
98 #define TYPE_MPART 0x01
99 #define TYPE_ETC 0x02
100 #define ENC_B8 0x00
101 #define ENC_QP 0x10
102 #define ENC_B64 0x20
104 struct mime {
105 int depth;
106 int part[MAXPARTS];
107 char bound[MAXPARTS][BOUNDLEN];
108 char *src;
109 char *dst;
110 char *end;
113 static void copy_till(struct mime *m, char *s)
115 int len = s - m->src;
116 memcpy(m->dst, m->src, len);
117 m->dst += len;
118 m->src += len;
121 static int starts(char *r, char *s)
123 while (*s)
124 if (tolower(*s++) != tolower(*r++))
125 return 0;
126 return 1;
129 static void read_boundary(struct mime *m, char *s)
131 char *bound = m->bound[m->depth];
132 char *e;
133 s = memchr(s, '=', m->end - s);
134 if (!s)
135 return;
136 s++;
137 if (*s == '"') {
138 s++;
139 e = memchr(s, '"', m->end - s);
140 } else {
141 e = s;
142 while (e < m->end && !isspace(*e))
143 e++;
145 if (!e)
146 return;
147 bound[0] = '-';
148 bound[1] = '-';
149 memcpy(bound + 2, s, e - s);
150 bound[e - s + 2] = '\0';
151 m->depth++;
154 static int read_hdrs(struct mime *m)
156 char *s = m->src;
157 char *e = m->end;
158 int type = 0;
159 while (s && s < e && *s != '\n') {
160 char *n = memchr(s, '\n', e - s);
161 while (n && n + 1 < e && n[1] != '\n' && isspace(n[1]))
162 n = memchr(n + 1, '\n', e - n - 1);
163 if (!n)
164 break;
165 n++;
166 copy_till(m, s);
167 if (starts(s, "Subject:") || starts(s, "From:")) {
168 m->dst = dec_qp(m->dst, m->src, n - s);
169 m->src = n;
171 if (starts(s, "Content-Type:")) {
172 char *t = strchr(s, ':') + 1;
173 while (isspace(*t) && t < e)
174 t++;
175 if (starts(t, "text"))
176 type |= TYPE_TXT;
177 if (starts(t, "multipart")) {
178 type |= TYPE_MPART;
179 read_boundary(m, t);
182 if (starts(s, "Content-Transfer-Encoding:")) {
183 char *t = strchr(s, ':') + 1;
184 while (isspace(*t) && t < e)
185 t++;
186 if (starts(t, "quoted-printable"))
187 type |= ENC_QP;
188 if (starts(t, "base64"))
189 type |= ENC_B64;
192 s = n;
194 copy_till(m, s ? s + 1 : e);
195 return type;
198 static int is_bound(struct mime *m, char *s)
200 return starts(s, m->bound[m->depth - 1]);
203 static void read_bound(struct mime *m)
205 char *s = m->src;
206 int len = strlen(m->bound[m->depth - 1]);
207 if (s[len] == '-' && s[len + 1] == '-')
208 m->depth--;
209 s = memchr(s, '\n', m->end - s);
210 s = s ? s + 1 : m->end;
211 copy_till(m, s);
214 static char *find_bound(struct mime *m)
216 char *s = m->src;
217 char *e = m->end;
218 while (s < e) {
219 if (is_bound(m, s))
220 return s;
221 if (!(s = memchr(s, '\n', e - s)))
222 break;
223 s++;
225 return e;
228 static void read_body(struct mime *m, int type)
230 char *end = m->depth ? find_bound(m) : m->end;
231 if (~type & TYPE_TXT) {
232 copy_till(m, end);
233 return;
235 if (type & ENC_QP) {
236 m->dst = dec_qp(m->dst, m->src, end - m->src);
237 m->src = end;
238 return;
240 if (type & ENC_B64) {
241 m->dst = dec_b64(m->dst, m->src, end - m->src);
242 m->src = end;
243 return;
245 copy_till(m, end);
248 int mime_decode(char *dst, char *src, int len)
250 struct mime m;
251 memset(&m, 0, sizeof(m));
252 m.src = src;
253 m.dst = dst;
254 m.end = src + len;
255 while ((m.depth && m.src < m.end) || m.src == src) {
256 int type = read_hdrs(&m);
257 read_body(&m, type);
258 if (m.depth)
259 read_bound(&m);
261 *m.dst = '\0';
262 return m.dst - dst;