mk: sort messages by date or thread
[neatmail.git] / mime.c
blob4e9f02fc9eaf0b70dad1cc10a51b505ca12ff87b
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 + 2 < e) {
68 sbuf_chr(sb, (hexval(s[1]) << 4) | hexval(s[2]));
69 s += 3;
70 } else {
71 sbuf_chr(sb, *s == '_' ? ' ' : (unsigned char) *s);
72 s++;
77 static void msg_hdrdec2(struct sbuf *sb, char *hdr, char *end)
79 while (hdr < end) {
80 char *q1 = hdr[0] == '=' && hdr[1] == '?' ? hdr + 1 : NULL;
81 char *q2 = q1 ? memchr(q1 + 1, '?', end - q1) : NULL;
82 char *q3 = q2 ? memchr(q2 + 1, '?', end - q2) : NULL;
83 char *q4 = q3 ? memchr(q3 + 1, '?', end - q3) : NULL;
84 if (q1 && q2 && q3 && q4 && q4[1] == '=') {
85 int c = tolower((unsigned char) q2[1]);
86 if (c == 'b')
87 dec_b64(sb, q3 + 1, q4);
88 else
89 dec_qp(sb, q3 + 1, q4);
90 hdr = q4 + 2;
91 while (isspace((unsigned char) *hdr) && hdr + 1 < end)
92 hdr++;
93 } else {
94 sbuf_chr(sb, (unsigned char) *hdr++);
99 char *msg_hdrdec(char *hdr)
101 struct sbuf *sb;
102 sb = sbuf_make();
103 msg_hdrdec2(sb, hdr, strchr(hdr, '\0'));
104 return sbuf_done(sb);
107 /* decoding mime messages */
109 #define MAXPARTS (1 << 3)
110 #define BOUNDLEN (1 << 7)
112 #define TYPE_TXT 0x01
113 #define TYPE_MPART 0x02
114 #define TYPE_ETC 0x04
115 #define ENC_B8 0x10
116 #define ENC_QP 0x20
117 #define ENC_B64 0x40
119 struct mime {
120 int depth;
121 int part[MAXPARTS];
122 char bound[MAXPARTS][BOUNDLEN];
123 char *src;
124 char *dst;
125 char *end;
128 static void copy_till(struct mime *m, struct sbuf *dst, char *s)
130 int len = s - m->src;
131 sbuf_mem(dst, m->src, len);
132 m->src += len;
135 static void read_boundary(struct mime *m, char *s, char *hdrend)
137 char *bound = m->bound[m->depth];
138 char *e;
139 s = memchr(s, '=', hdrend - s);
140 if (!s)
141 return;
142 s++;
143 if (*s == '"') {
144 s++;
145 e = memchr(s, '"', hdrend - s);
146 } else {
147 e = s;
148 while (e < hdrend && !isspace(*e) && *e != ';')
149 e++;
151 if (!e)
152 return;
153 bound[0] = '-';
154 bound[1] = '-';
155 memcpy(bound + 2, s, e - s);
156 bound[e - s + 2] = '\0';
157 m->depth++;
160 static char *hdr_nextfield(char *s, char *e)
162 while (s && s < e && *s != ';')
163 if (*s++ == '"')
164 if ((s = memchr(s, '"', e - s)))
165 s++;
166 return s && s + 2 < e ? s + 1 : NULL;
169 static int read_hdrs(struct mime *m, struct sbuf *dst)
171 char *s = m->src;
172 char *e = m->end;
173 int type = 0;
174 while (s && s < e && *s != '\n') {
175 char *n = memchr(s, '\n', e - s);
176 while (n && n + 1 < e && n[1] != '\n' && isspace(n[1]))
177 n = memchr(n + 1, '\n', e - n - 1);
178 if (!n++)
179 break;
180 if (startswith(s, "Content-Type:")) {
181 char *key = strchr(s, ':') + 1;
182 char *hdrend = s + hdrlen(s, e - s);
183 while (key) {
184 while (key < hdrend && isspace(*key))
185 key++;
186 if (startswith(key, "text"))
187 type |= TYPE_TXT;
188 if (startswith(key, "multipart"))
189 type |= TYPE_MPART;
190 if (startswith(key, "boundary"))
191 read_boundary(m, key, hdrend);
192 key = hdr_nextfield(key, hdrend);
195 if (startswith(s, "Content-Transfer-Encoding:")) {
196 char *key = strchr(s, ':') + 1;
197 char *hdrend = s + hdrlen(s, e - s);
198 while (key) {
199 while (key < hdrend && isspace(*key))
200 key++;
201 if (startswith(key, "quoted-printable"))
202 type |= ENC_QP;
203 if (startswith(key, "base64"))
204 type |= ENC_B64;
205 key = hdr_nextfield(key, hdrend);
208 msg_hdrdec2(dst, s, n);
209 s = n;
211 sbuf_chr(dst, '\n');
212 m->src = s + 1;
213 return type;
216 static int is_bound(struct mime *m, char *s)
218 return startswith(s, m->bound[m->depth - 1]);
221 static void read_bound(struct mime *m, struct sbuf *dst)
223 char *s = m->src;
224 int len = strlen(m->bound[m->depth - 1]);
225 if (s[len] == '-' && s[len + 1] == '-')
226 m->depth--;
227 s = memchr(s, '\n', m->end - s);
228 s = s ? s + 1 : m->end;
229 copy_till(m, dst, s);
232 static char *find_bound(struct mime *m)
234 char *s = m->src;
235 char *e = m->end;
236 while (s < e) {
237 if (is_bound(m, s))
238 return s;
239 if (!(s = memchr(s, '\n', e - s)))
240 break;
241 s++;
243 return e;
246 static void read_body(struct mime *m, struct sbuf *dst, int type)
248 char *end = m->depth ? find_bound(m) : m->end;
249 if (~type & TYPE_TXT) {
250 copy_till(m, dst, end);
251 return;
253 if (type & ENC_QP) {
254 dec_qp(dst, m->src, end);
255 m->src = end;
256 return;
258 if (type & ENC_B64) {
259 dec_b64(dst, m->src, end);
260 m->src = end;
261 return;
263 copy_till(m, dst, end);
266 int msg_demime(char *msg, long msglen, char **mod, long *modlen)
268 struct sbuf *dst = sbuf_make();
269 struct mime m;
270 m.src = msg;
271 m.end = msg + msglen;
272 while ((m.depth && m.src < m.end) || m.src == msg) {
273 int type = read_hdrs(&m, dst);
274 read_body(&m, dst, type);
275 if (m.depth)
276 read_bound(&m, dst);
278 sbuf_chr(dst, '\0');
279 *modlen = sbuf_len(dst) - 1;
280 *mod = sbuf_done(dst);
281 return 0;