mbox: check the message number in mbox_set()
[neatmail.git] / mime.c
bloba8f21164eebf42110fe546459e0455193c93f3d5
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 *dst;
127 char *end;
130 static void copy_till(struct mime *m, struct sbuf *dst, char *s)
132 int len = s - m->src;
133 sbuf_mem(dst, m->src, len);
134 m->src += len;
137 static void read_boundary(struct mime *m, char *s, char *hdrend)
139 char *bound = m->bound[m->depth];
140 char *e;
141 s = memchr(s, '=', hdrend - s);
142 if (!s)
143 return;
144 s++;
145 if (*s == '"') {
146 s++;
147 e = memchr(s, '"', hdrend - s);
148 } else {
149 e = s;
150 while (e < hdrend && !isspace(*e) && *e != ';')
151 e++;
153 if (!e)
154 return;
155 bound[0] = '-';
156 bound[1] = '-';
157 memcpy(bound + 2, s, e - s);
158 bound[e - s + 2] = '\0';
159 m->depth++;
162 static char *hdr_nextfield(char *s, char *e)
164 while (s && s < e && *s != ';')
165 if (*s++ == '"')
166 if ((s = memchr(s, '"', e - s)))
167 s++;
168 return s && s + 2 < e ? s + 1 : NULL;
171 static int read_hdrs(struct mime *m, struct sbuf *dst)
173 char *s = m->src;
174 char *e = m->end;
175 int type = 0;
176 while (s && s < e && *s != '\n') {
177 char *n = memchr(s, '\n', e - s);
178 while (n && n + 1 < e && n[1] != '\n' && isspace(n[1]))
179 n = memchr(n + 1, '\n', e - n - 1);
180 if (!n++)
181 break;
182 if (startswith(s, "Content-Type:")) {
183 char *key = strchr(s, ':') + 1;
184 char *hdrend = s + hdrlen(s, e - s);
185 while (key) {
186 while (key < hdrend && isspace(*key))
187 key++;
188 if (startswith(key, "text"))
189 type |= TYPE_TXT;
190 if (startswith(key, "multipart"))
191 type |= TYPE_MPART;
192 if (startswith(key, "boundary"))
193 read_boundary(m, key, hdrend);
194 key = hdr_nextfield(key, hdrend);
197 if (startswith(s, "Content-Transfer-Encoding:")) {
198 char *key = strchr(s, ':') + 1;
199 char *hdrend = s + hdrlen(s, e - s);
200 while (key) {
201 while (key < hdrend && isspace(*key))
202 key++;
203 if (startswith(key, "quoted-printable"))
204 type |= ENC_QP;
205 if (startswith(key, "base64"))
206 type |= ENC_B64;
207 key = hdr_nextfield(key, hdrend);
210 msg_hdrdec2(dst, s, n);
211 s = n;
213 sbuf_chr(dst, '\n');
214 m->src = s + 1;
215 return type;
218 static int is_bound(struct mime *m, char *s)
220 return startswith(s, m->bound[m->depth - 1]);
223 static void read_bound(struct mime *m, struct sbuf *dst)
225 char *s = m->src;
226 int len = strlen(m->bound[m->depth - 1]);
227 if (s[len] == '-' && s[len + 1] == '-')
228 m->depth--;
229 s = memchr(s, '\n', m->end - s);
230 s = s ? s + 1 : m->end;
231 copy_till(m, dst, s);
234 static char *find_bound(struct mime *m)
236 char *s = m->src;
237 char *e = m->end;
238 while (s < e) {
239 if (is_bound(m, s))
240 return s;
241 if (!(s = memchr(s, '\n', e - s)))
242 break;
243 s++;
245 return e;
248 static void read_body(struct mime *m, struct sbuf *dst, int type)
250 char *end = m->depth ? find_bound(m) : m->end;
251 if (~type & TYPE_TXT) {
252 copy_till(m, dst, end);
253 return;
255 if (type & ENC_QP) {
256 dec_qp(dst, m->src, end);
257 m->src = end;
258 return;
260 if (type & ENC_B64) {
261 dec_b64(dst, m->src, end);
262 sbuf_chr(dst, '\n');
263 m->src = end;
264 return;
266 copy_till(m, dst, end);
269 int msg_demime(char *msg, long msglen, char **mod, long *modlen)
271 struct sbuf *dst = sbuf_make();
272 struct mime m;
273 m.src = msg;
274 m.end = msg + msglen;
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;