mime: fix uninitialised mime->depth
[neatmail.git] / mime.c
blob0aa1ff8844352728faaae6b405d8d3c1f7e1e738
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include "mail.h"
7 static char *b64_ch =
8 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
9 static int b64_val[256];
11 static void b64_init(void)
13 int i;
14 for (i = 0; i < 64; i++)
15 b64_val[(unsigned char) b64_ch[i]] = i;
18 static int b64_dec(char *d, char *s)
20 unsigned v = 0;
21 v |= b64_val[(unsigned char) s[0]];
22 v <<= 6;
23 v |= b64_val[(unsigned char) s[1]];
24 v <<= 6;
25 v |= b64_val[(unsigned char) s[2]];
26 v <<= 6;
27 v |= b64_val[(unsigned char) s[3]];
29 d[2] = v & 0xff;
30 v >>= 8;
31 d[1] = v & 0xff;
32 v >>= 8;
33 d[0] = v & 0xff;
34 return 3 - (s[1] == '=') - (s[2] == '=') - (s[3] == '=');
37 static void dec_b64(struct sbuf *sb, char *s, char *e)
39 if (!b64_val['B'])
40 b64_init();
41 while (s + 4 <= e) {
42 while (s < e && isspace((unsigned char) *s))
43 s++;
44 if (s < e) {
45 char dst[4];
46 int n = b64_dec(dst, s);
47 s += 4;
48 sbuf_mem(sb, dst, n);
53 static int hexval(int c)
55 if (c >= '0' && c <= '9')
56 return c - '0';
57 if (c >= 'A' && c <= 'F')
58 return 10 + c - 'A';
59 if (c >= 'a' && c <= 'f')
60 return 10 + c - 'a';
61 return 0;
64 static void dec_qp(struct sbuf *sb, char *s, char *e)
66 while (s < e) {
67 if (*s == '=' && s[1] == '\n') {
68 s += 2;
69 } else if (*s == '=' && s + 2 < e) {
70 sbuf_chr(sb, (hexval(s[1]) << 4) | hexval(s[2]));
71 s += 3;
72 } else {
73 sbuf_chr(sb, *s == '_' ? ' ' : (unsigned char) *s);
74 s++;
79 static void msg_hdrdec2(struct sbuf *sb, char *hdr, char *end)
81 while (hdr < end) {
82 char *q1 = hdr[0] == '=' && hdr[1] == '?' ? hdr + 1 : NULL;
83 char *q2 = q1 ? memchr(q1 + 1, '?', end - q1) : NULL;
84 char *q3 = q2 ? memchr(q2 + 1, '?', end - q2) : NULL;
85 char *q4 = q3 ? memchr(q3 + 1, '?', end - q3) : NULL;
86 if (q1 && q2 && q3 && q4 && q4[1] == '=') {
87 int c = tolower((unsigned char) q2[1]);
88 if (c == 'b')
89 dec_b64(sb, q3 + 1, q4);
90 else
91 dec_qp(sb, q3 + 1, q4);
92 hdr = q4 + 2;
93 while (isspace((unsigned char) *hdr) && hdr + 1 < end)
94 hdr++;
95 } else {
96 sbuf_chr(sb, (unsigned char) *hdr++);
101 char *msg_hdrdec(char *hdr)
103 struct sbuf *sb;
104 sb = sbuf_make();
105 msg_hdrdec2(sb, hdr, strchr(hdr, '\0'));
106 return sbuf_done(sb);
109 /* decoding mime messages */
111 #define MAXPARTS (1 << 3)
112 #define BOUNDLEN (1 << 7)
114 #define TYPE_TXT 0x01
115 #define TYPE_MPART 0x02
116 #define TYPE_ETC 0x04
117 #define ENC_B8 0x10
118 #define ENC_QP 0x20
119 #define ENC_B64 0x40
121 struct mime {
122 int depth;
123 int part[MAXPARTS];
124 char bound[MAXPARTS][BOUNDLEN];
125 char *src;
126 char *end;
129 static void copy_till(struct mime *m, struct sbuf *dst, char *s)
131 int len = s - m->src;
132 sbuf_mem(dst, m->src, len);
133 m->src += len;
136 static void read_boundary(struct mime *m, char *s, char *hdrend)
138 char *bound = m->bound[m->depth];
139 char *e;
140 s = memchr(s, '=', hdrend - s);
141 if (!s)
142 return;
143 s++;
144 if (*s == '"') {
145 s++;
146 e = memchr(s, '"', hdrend - s);
147 } else {
148 e = s;
149 while (e < hdrend && !isspace(*e) && *e != ';')
150 e++;
152 if (!e)
153 return;
154 bound[0] = '-';
155 bound[1] = '-';
156 memcpy(bound + 2, s, e - s);
157 bound[e - s + 2] = '\0';
158 m->depth++;
161 static char *hdr_nextfield(char *s, char *e)
163 while (s && s < e && *s != ';')
164 if (*s++ == '"')
165 if ((s = memchr(s, '"', e - s)))
166 s++;
167 return s && s + 2 < e ? s + 1 : NULL;
170 static int read_hdrs(struct mime *m, struct sbuf *dst)
172 char *s = m->src;
173 char *e = m->end;
174 int type = 0;
175 while (s && s < e && *s != '\n') {
176 char *n = memchr(s, '\n', e - s);
177 while (n && n + 1 < e && n[1] != '\n' && isspace(n[1]))
178 n = memchr(n + 1, '\n', e - n - 1);
179 if (!n++)
180 break;
181 if (startswith(s, "Content-Type:")) {
182 char *key = strchr(s, ':') + 1;
183 char *hdrend = s + hdrlen(s, e - s);
184 while (key) {
185 while (key < hdrend && isspace(*key))
186 key++;
187 if (startswith(key, "text"))
188 type |= TYPE_TXT;
189 if (startswith(key, "multipart"))
190 type |= TYPE_MPART;
191 if (startswith(key, "boundary"))
192 read_boundary(m, key, hdrend);
193 key = hdr_nextfield(key, hdrend);
196 if (startswith(s, "Content-Transfer-Encoding:")) {
197 char *key = strchr(s, ':') + 1;
198 char *hdrend = s + hdrlen(s, e - s);
199 while (key) {
200 while (key < hdrend && isspace(*key))
201 key++;
202 if (startswith(key, "quoted-printable"))
203 type |= ENC_QP;
204 if (startswith(key, "base64"))
205 type |= ENC_B64;
206 key = hdr_nextfield(key, hdrend);
209 msg_hdrdec2(dst, s, n);
210 s = n;
212 sbuf_chr(dst, '\n');
213 m->src = s + 1;
214 return type;
217 static int is_bound(struct mime *m, char *s)
219 return startswith(s, m->bound[m->depth - 1]);
222 static void read_bound(struct mime *m, struct sbuf *dst)
224 char *s = m->src;
225 int len = strlen(m->bound[m->depth - 1]);
226 if (s[len] == '-' && s[len + 1] == '-')
227 m->depth--;
228 s = memchr(s, '\n', m->end - s);
229 s = s ? s + 1 : m->end;
230 copy_till(m, dst, s);
233 static char *find_bound(struct mime *m)
235 char *s = m->src;
236 char *e = m->end;
237 while (s < e) {
238 if (is_bound(m, s))
239 return s;
240 if (!(s = memchr(s, '\n', e - s)))
241 break;
242 s++;
244 return e;
247 static void read_body(struct mime *m, struct sbuf *dst, int type)
249 char *end = m->depth ? find_bound(m) : m->end;
250 if (~type & TYPE_TXT) {
251 copy_till(m, dst, end);
252 return;
254 if (type & ENC_QP) {
255 dec_qp(dst, m->src, end);
256 m->src = end;
257 return;
259 if (type & ENC_B64) {
260 dec_b64(dst, m->src, end);
261 sbuf_chr(dst, '\n');
262 m->src = end;
263 return;
265 copy_till(m, dst, end);
268 int msg_demime(char *msg, long msglen, char **mod, long *modlen)
270 struct sbuf *dst = sbuf_make();
271 struct mime m;
272 m.src = msg;
273 m.end = msg + msglen;
274 m.depth = 0;
275 while ((m.depth && m.src < m.end) || m.src == msg) {
276 int type = read_hdrs(&m, dst);
277 read_body(&m, dst, type);
278 if (m.depth)
279 read_bound(&m, dst);
281 sbuf_chr(dst, '\0');
282 *modlen = sbuf_len(dst) - 1;
283 *mod = sbuf_done(dst);
284 return 0;