1 /* $Id$ */
2 
3 /*
4  * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <netinet/in.h>
21 #include <arpa/nameser.h>
22 
23 #include <ctype.h>
24 #include <resolv.h>
25 #include <string.h>
26 
27 #include <openssl/bio.h>
28 #include <openssl/buffer.h>
29 #include <openssl/evp.h>
30 #include <openssl/hmac.h>
31 
32 #include "fdm.h"
33 #include "fetch.h"
34 
35 void	imap_free(void *);
36 
37 int	imap_parse(struct account *, int, char *);
38 
39 char   *imap_base64_encode(char *);
40 char   *imap_base64_decode(char *);
41 
42 int	imap_pick_auth(struct account *, struct fetch_ctx *);
43 
44 int	imap_state_connect(struct account *, struct fetch_ctx *);
45 int	imap_state_capability1(struct account *, struct fetch_ctx *);
46 int	imap_state_capability2(struct account *, struct fetch_ctx *);
47 int	imap_state_starttls(struct account *, struct fetch_ctx *);
48 int	imap_state_cram_md5_auth(struct account *, struct fetch_ctx *);
49 int	imap_state_login(struct account *, struct fetch_ctx *);
50 int	imap_state_user(struct account *, struct fetch_ctx *);
51 int	imap_state_pass(struct account *, struct fetch_ctx *);
52 int	imap_state_select2(struct account *, struct fetch_ctx *);
53 int	imap_state_select3(struct account *, struct fetch_ctx *);
54 int	imap_state_select4(struct account *, struct fetch_ctx *);
55 int	imap_state_search1(struct account *, struct fetch_ctx *);
56 int	imap_state_search2(struct account *, struct fetch_ctx *);
57 int	imap_state_search3(struct account *, struct fetch_ctx *);
58 int	imap_state_next(struct account *, struct fetch_ctx *);
59 int	imap_state_body(struct account *, struct fetch_ctx *);
60 int	imap_state_line(struct account *, struct fetch_ctx *);
61 int	imap_state_mail(struct account *, struct fetch_ctx *);
62 int	imap_state_gmext_start(struct account *, struct fetch_ctx *);
63 int	imap_state_gmext_body(struct account *, struct fetch_ctx *);
64 int	imap_state_gmext_done(struct account *, struct fetch_ctx *);
65 int	imap_state_commit(struct account *, struct fetch_ctx *);
66 int	imap_state_expunge(struct account *, struct fetch_ctx *);
67 int	imap_state_close(struct account *, struct fetch_ctx *);
68 int	imap_state_quit(struct account *, struct fetch_ctx *);
69 
70 /* Put line to server. */
71 int
imap_putln(struct account * a,const char * fmt,...)72 imap_putln(struct account *a, const char *fmt, ...)
73 {
74 	struct fetch_imap_data	*data = a->data;
75 	va_list			 ap;
76 	int			 n;
77 
78 	va_start(ap, fmt);
79 	n = data->putln(a, fmt, ap);
80 	va_end(ap);
81 
82 	return (n);
83 }
84 
85 /*
86  * Get line from server. Returns -1 on error, 0 on success, a NULL line when
87  * out of data.
88  */
89 int
imap_getln(struct account * a,struct fetch_ctx * fctx,int type,char ** line)90 imap_getln(struct account *a, struct fetch_ctx *fctx, int type, char **line)
91 {
92 	struct fetch_imap_data	*data = a->data;
93 	int			 n;
94 
95 	do {
96 		if (data->getln(a, fctx, line) != 0)
97 			return (-1);
98 		if (*line == NULL)
99 			return (0);
100 	} while ((n = imap_parse(a, type, *line)) == 1);
101 	return (n);
102 }
103 
104 /* Free auxiliary data. */
105 void
imap_free(void * ptr)106 imap_free(void *ptr)
107 {
108 	xfree(ptr);
109 }
110 
111 /* Check for okay from server. */
112 int
imap_okay(char * line)113 imap_okay(char *line)
114 {
115 	char	*ptr;
116 
117 	ptr = strchr(line, ' ');
118 	if (ptr == NULL)
119 		return (0);
120 	if (ptr[1] != 'O' || ptr[2] != 'K' || (ptr[3] != ' ' && ptr[3] != '\0'))
121 		return (0);
122 	return (1);
123 }
124 
125 /* Check for no from server. */
126 int
imap_no(char * line)127 imap_no(char *line)
128 {
129 	char	*ptr;
130 
131 	ptr = strchr(line, ' ');
132 	if (ptr == NULL)
133 		return (0);
134 	if (ptr[1] != 'N' || ptr[2] != 'O' || (ptr[3] != ' ' && ptr[3] != '\0'))
135 		return (0);
136 	return (1);
137 }
138 
139 /*
140  * Parse line based on type. Returns -1 on error, 0 on success, 1 to ignore
141  * this line.
142  */
143 int
imap_parse(struct account * a,int type,char * line)144 imap_parse(struct account *a, int type, char *line)
145 {
146 	struct fetch_imap_data	*data = a->data;
147 	int			 tag;
148 
149 	if (type == IMAP_RAW)
150 		return (0);
151 
152 	tag = imap_tag(line);
153 	switch (type) {
154 	case IMAP_TAGGED:
155 		if (tag == IMAP_TAG_NONE)
156 			return (1);
157 		if (tag == IMAP_TAG_CONTINUE)
158 			goto invalid;
159 		if (tag != data->tag)
160 			goto invalid;
161 		break;
162 	case IMAP_UNTAGGED:
163 		if (tag != IMAP_TAG_NONE)
164 			goto invalid;
165 		break;
166 	case IMAP_CONTINUE:
167 		if (tag == IMAP_TAG_NONE)
168 			return (1);
169 		if (tag != IMAP_TAG_CONTINUE)
170 			goto invalid;
171 		break;
172 	}
173 
174 	return (0);
175 
176 invalid:
177 	imap_bad(a, line);
178 	return (-1);
179 }
180 
181 /* Parse IMAP tag. */
182 int
imap_tag(char * line)183 imap_tag(char *line)
184 {
185 	int		 tag;
186 	const char	*errstr;
187 	char		*ptr;
188 
189 	if (line[0] == '*' && line[1] == ' ')
190 		return (IMAP_TAG_NONE);
191 	if (line[0] == '+')
192 		return (IMAP_TAG_CONTINUE);
193 
194 	if ((ptr = strchr(line, ' ')) == NULL)
195 		ptr = strchr(line, '\0');
196 	*ptr = '\0';
197 
198 	tag = strtonum(line, 0, INT_MAX, &errstr);
199 	*ptr = ' ';
200 	if (errstr != NULL)
201 		return (IMAP_TAG_ERROR);
202 
203 	return (tag);
204 }
205 
206 /* Base64 encode string. */
207 char *
imap_base64_encode(char * in)208 imap_base64_encode(char *in)
209 {
210 	char	*out;
211 	size_t	 size;
212 
213 	size = (strlen(in) * 2) + 1;
214 	out = xcalloc(1, size);
215 	if (b64_ntop(in, strlen(in), out, size) < 0) {
216 		xfree(out);
217 		return (NULL);
218 	}
219 	return (out);
220 }
221 
222 /* Base64 decode string. */
223 char *
imap_base64_decode(char * in)224 imap_base64_decode(char *in)
225 {
226 	char	*out;
227 	size_t	 size;
228 
229 	size = (strlen(in) * 4) + 1;
230 	out = xcalloc(1, size);
231 	if (b64_pton(in, out, size) < 0) {
232 		xfree(out);
233 		return (NULL);
234 	}
235 	return (out);
236 }
237 
238 int
imap_bad(struct account * a,const char * line)239 imap_bad(struct account *a, const char *line)
240 {
241 	log_warnx("%s: unexpected data: %s", a->name, line);
242 	return (FETCH_ERROR);
243 }
244 
245 int
imap_invalid(struct account * a,const char * line)246 imap_invalid(struct account *a, const char *line)
247 {
248 	log_warnx("%s: invalid response: %s", a->name, line);
249 	return (FETCH_ERROR);
250 }
251 
252 /* Commit mail. */
253 int
imap_commit(struct account * a,struct mail * m)254 imap_commit(struct account *a, struct mail *m)
255 {
256 	struct fetch_imap_data	*data = a->data;
257 	struct fetch_imap_mail	*aux = m->auxdata;
258 
259 	if (m->decision == DECISION_DROP)
260 		ARRAY_ADD(&data->dropped, aux->uid);
261 	else
262 		ARRAY_ADD(&data->kept, aux->uid);
263 
264 	xfree(aux);
265 	m->auxdata = m->auxfree = NULL;
266 
267 	return (FETCH_AGAIN);
268 }
269 
270 /* Abort fetch. */
271 void
imap_abort(struct account * a)272 imap_abort(struct account *a)
273 {
274 	struct fetch_imap_data	*data = a->data;
275 
276 	ARRAY_FREE(&data->dropped);
277 	ARRAY_FREE(&data->kept);
278 	ARRAY_FREE(&data->wanted);
279 
280 	data->disconnect(a);
281 }
282 
283 /* Return total mails available. */
284 u_int
imap_total(struct account * a)285 imap_total(struct account *a)
286 {
287 	struct fetch_imap_data	*data = a->data;
288 
289 	return (data->folders_total);
290 }
291 
292 /* Try an authentication method. */
293 int
imap_pick_auth(struct account * a,struct fetch_ctx * fctx)294 imap_pick_auth(struct account *a, struct fetch_ctx *fctx)
295 {
296 	struct fetch_imap_data	*data = a->data;
297 
298 	/* Try CRAM-MD5, if server supports it and user allows it. */
299 	if (!data->nocrammd5 && (data->capa & IMAP_CAPA_AUTH_CRAM_MD5)) {
300 		if (imap_putln(a,
301 		    "%u AUTHENTICATE CRAM-MD5", ++data->tag) != 0)
302 			return (FETCH_ERROR);
303 		fctx->state = imap_state_cram_md5_auth;
304 		return (FETCH_BLOCK);
305 	}
306 
307 	/* Fall back to LOGIN, unless config disallows it. */
308 	if (!data->nologin) {
309 		if (imap_putln(a,
310 		    "%u LOGIN {%zu}", ++data->tag, strlen(data->user)) != 0)
311 			return (FETCH_ERROR);
312 		fctx->state = imap_state_login;
313 		return (FETCH_BLOCK);
314 	}
315 
316 	log_warnx("%s: no authentication methods", a->name);
317 	return (FETCH_ERROR);
318 }
319 
320 /* Common initialisation state. */
321 int
imap_state_init(struct account * a,struct fetch_ctx * fctx)322 imap_state_init(struct account *a, struct fetch_ctx *fctx)
323 {
324 	struct fetch_imap_data	*data = a->data;
325 
326 	ARRAY_INIT(&data->dropped);
327 	ARRAY_INIT(&data->kept);
328 	ARRAY_INIT(&data->wanted);
329 
330 	data->tag = 0;
331 
332 	data->folder = 0;
333 	data->folders_total = 0;
334 
335 	fctx->state = imap_state_connect;
336 	return (FETCH_AGAIN);
337 }
338 
339 /* Connect state. */
340 int
imap_state_connect(struct account * a,struct fetch_ctx * fctx)341 imap_state_connect(struct account *a, struct fetch_ctx *fctx)
342 {
343 	struct fetch_imap_data	*data = a->data;
344 
345 	if (data->connect(a) != 0)
346 		return (FETCH_ERROR);
347 
348 	fctx->state = imap_state_connected;
349 	return (FETCH_BLOCK);
350 }
351 
352 /* Connected state: wait for initial line from server. */
353 int
imap_state_connected(struct account * a,struct fetch_ctx * fctx)354 imap_state_connected(struct account *a, struct fetch_ctx *fctx)
355 {
356 	struct fetch_imap_data	*data = a->data;
357 	char			*line;
358 
359 	if (imap_getln(a, fctx, IMAP_UNTAGGED, &line) != 0)
360 		return (FETCH_ERROR);
361 	if (line == NULL)
362 		return (FETCH_BLOCK);
363 
364 	if (strncmp(line, "* PREAUTH", 9) == 0) {
365 		fctx->state = imap_state_select1;
366 		return (FETCH_AGAIN);
367 	}
368 	if (data->user == NULL || data->pass == NULL) {
369 		log_warnx("%s: not PREAUTH and no user or password", a->name);
370 		return (FETCH_ERROR);
371 	}
372 
373 	if (imap_putln(a, "%u CAPABILITY", ++data->tag) != 0)
374 		return (FETCH_ERROR);
375 	fctx->state = imap_state_capability1;
376 	return (FETCH_BLOCK);
377 }
378 
379 /* Capability state 1. Parse capabilities and set flags. */
380 int
imap_state_capability1(struct account * a,struct fetch_ctx * fctx)381 imap_state_capability1(struct account *a, struct fetch_ctx *fctx)
382 {
383 	struct fetch_imap_data	*data = a->data;
384 	char			*line, *ptr;
385 
386 	if (imap_getln(a, fctx, IMAP_UNTAGGED, &line) != 0)
387 		return (FETCH_ERROR);
388 	if (line == NULL)
389 		return (FETCH_BLOCK);
390 
391 	/* Convert to uppercase. */
392 	for (ptr = line; *ptr != '\0'; ptr++)
393 		*ptr = toupper((u_char) *ptr);
394 
395 	if (strstr(line, "IMAP4REV1") == NULL) {
396 		log_warnx("%s: no IMAP4rev1 capability: %s", a->name, line);
397 		return (FETCH_ERROR);
398 	}
399 
400 	data->capa = 0;
401 	if (strstr(line, "AUTH=CRAM-MD5") != NULL)
402 		data->capa |= IMAP_CAPA_AUTH_CRAM_MD5;
403 
404 	/* Use XYZZY to detect Google brokenness. */
405 	if (strstr(line, "XYZZY") != NULL)
406 		data->capa |= IMAP_CAPA_XYZZY;
407 
408 	/* GMail IMAP extensions. */
409 	if (strstr(line, "X-GM-EXT-1") != NULL)
410 		data->capa |= IMAP_CAPA_GMEXT;
411 
412 	if (strstr(line, "STARTTLS") != NULL)
413 		data->capa |= IMAP_CAPA_STARTTLS;
414 
415 	fctx->state = imap_state_capability2;
416 	return (FETCH_AGAIN);
417 }
418 
419 /* Capability state 2. Check capabilities and choose login type. */
420 int
imap_state_capability2(struct account * a,struct fetch_ctx * fctx)421 imap_state_capability2(struct account *a, struct fetch_ctx *fctx)
422 {
423 	struct fetch_imap_data	*data = a->data;
424 	char			*line;
425 
426 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
427 		return (FETCH_ERROR);
428 	if (line == NULL)
429 		return (FETCH_BLOCK);
430 	if (!imap_okay(line))
431 		return (imap_bad(a, line));
432 
433 	if (data->starttls) {
434 		if (!(data->capa & IMAP_CAPA_STARTTLS)) {
435 			log_warnx("%s: server doesn't support STARTTLS",
436 			    a->name);
437 			return (FETCH_ERROR);
438 		}
439 		if (imap_putln(a, "%u STARTTLS", ++data->tag) != 0)
440 			return (FETCH_ERROR);
441 		fctx->state = imap_state_starttls;
442 		return (FETCH_BLOCK);
443 	}
444 
445 	return (imap_pick_auth(a, fctx));
446 }
447 
448 /* STARTTLS state. */
449 int
imap_state_starttls(struct account * a,struct fetch_ctx * fctx)450 imap_state_starttls(struct account *a, struct fetch_ctx *fctx)
451 {
452 	struct fetch_imap_data	*data = a->data;
453 	char			*line, *cause;
454 
455 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
456 		return (FETCH_ERROR);
457 	if (line == NULL)
458 		return (FETCH_BLOCK);
459 	if (!imap_okay(line))
460 		return (imap_bad(a, line));
461 
462 	data->io->ssl = makessl(&data->server, data->io->fd,
463 	    conf.verify_certs && data->server.verify, conf.timeout, &cause);
464 	if (data->io->ssl == NULL) {
465 		log_warnx("%s: STARTTLS failed: %s", a->name, cause);
466 		xfree(cause);
467 		return (FETCH_ERROR);
468 	}
469 
470 	return (imap_pick_auth(a, fctx));
471 }
472 
473 /* CRAM-MD5 auth state. */
474 int
imap_state_cram_md5_auth(struct account * a,struct fetch_ctx * fctx)475 imap_state_cram_md5_auth(struct account *a, struct fetch_ctx *fctx)
476 {
477 	struct fetch_imap_data	*data = a->data;
478 	char			*line, *ptr, *src, *b64;
479 	char			 out[EVP_MAX_MD_SIZE * 2 + 1];
480 	u_char			 digest[EVP_MAX_MD_SIZE];
481 	u_int			 i, n;
482 
483 	if (imap_getln(a, fctx, IMAP_CONTINUE, &line) != 0)
484 		return (FETCH_ERROR);
485 	if (line == NULL)
486 		return (FETCH_BLOCK);
487 
488 	ptr = line + 1;
489 	while (isspace((u_char) *ptr))
490 		ptr++;
491 	if (*ptr == '\0')
492 		return (imap_invalid(a, line));
493 
494 	b64 = imap_base64_decode(ptr);
495 	HMAC(EVP_md5(),
496 	    data->pass, strlen(data->pass), b64, strlen(b64), digest, &n);
497 	xfree(b64);
498 
499 	for (i = 0; i < n; i++)
500 		xsnprintf(out + i * 2, 3, "%02hhx", digest[i]);
501 	xasprintf(&src, "%s %s", data->user, out);
502 	b64 = imap_base64_encode(src);
503 	xfree(src);
504 
505 	if (imap_putln(a, "%s", b64) != 0) {
506 		xfree(b64);
507 		return (FETCH_ERROR);
508 	}
509 	xfree(b64);
510 
511 	fctx->state = imap_state_pass;
512 	return (FETCH_BLOCK);
513 }
514 
515 /* Login state. */
516 int
imap_state_login(struct account * a,struct fetch_ctx * fctx)517 imap_state_login(struct account *a, struct fetch_ctx *fctx)
518 {
519 	struct fetch_imap_data	*data = a->data;
520 	char			*line;
521 	size_t			 passlen;
522 
523 	if (imap_getln(a, fctx, IMAP_CONTINUE, &line) != 0)
524 		return (FETCH_ERROR);
525 	if (line == NULL)
526 		return (FETCH_BLOCK);
527 
528 	passlen = strlen(data->pass);
529 	if (data->capa & IMAP_CAPA_NOSPACE) {
530 		if (imap_putln(a, "%s{%zu}", data->user, passlen) != 0)
531 			return (FETCH_ERROR);
532 	} else {
533 		if (imap_putln(a, "%s {%zu}", data->user, passlen) != 0)
534 			return (FETCH_ERROR);
535 	}
536 	fctx->state = imap_state_user;
537 	return (FETCH_BLOCK);
538 }
539 
540 /* User state. */
541 int
imap_state_user(struct account * a,struct fetch_ctx * fctx)542 imap_state_user(struct account *a, struct fetch_ctx *fctx)
543 {
544 	struct fetch_imap_data	*data = a->data;
545 	char			*line;
546 	int                      tag;
547 
548 	if (data->capa & IMAP_CAPA_NOSPACE) {
549 		if (imap_getln(a, fctx, IMAP_CONTINUE, &line) != 0)
550 			return (FETCH_ERROR);
551 		if (line == NULL)
552 			return (FETCH_BLOCK);
553 	} else {
554 		for (;;) {
555 			if (imap_getln(a, fctx, IMAP_RAW, &line) != 0)
556 				return (FETCH_ERROR);
557 			if (line == NULL)
558 				return (FETCH_BLOCK);
559 			tag = imap_tag(line);
560 			if (tag == IMAP_TAG_NONE)
561 				continue;
562 			if (tag == IMAP_TAG_CONTINUE || tag == data->tag)
563 				break;
564 			return (FETCH_ERROR);
565 		}
566 		if (tag != IMAP_TAG_CONTINUE) {
567 			log_debug("%s: didn't accept user (%s); "
568 			    "trying without space", a->name, line);
569 			data->capa |= IMAP_CAPA_NOSPACE;
570 			return (imap_pick_auth(a, fctx));
571 		}
572 	}
573 
574 	if (imap_putln(a, "%s", data->pass) != 0)
575 		return (FETCH_ERROR);
576 	fctx->state = imap_state_pass;
577 	return (FETCH_BLOCK);
578 }
579 
580 /* Pass state. */
581 int
imap_state_pass(struct account * a,struct fetch_ctx * fctx)582 imap_state_pass(struct account *a, struct fetch_ctx *fctx)
583 {
584 	char	*line;
585 
586 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
587 		return (FETCH_ERROR);
588 	if (line == NULL)
589 		return (FETCH_BLOCK);
590 	if (!imap_okay(line))
591 		return (imap_bad(a, line));
592 
593 	fctx->state = imap_state_select1;
594 	return (FETCH_AGAIN);
595 }
596 
597 /* Select state 1. */
598 int
imap_state_select1(struct account * a,struct fetch_ctx * fctx)599 imap_state_select1(struct account *a, struct fetch_ctx *fctx)
600 {
601 	struct fetch_imap_data	*data = a->data;
602 
603 	if (imap_putln(a, "%u SELECT {%zu}",
604 	    ++data->tag, strlen(ARRAY_ITEM(data->folders, data->folder))) != 0)
605 		return (FETCH_ERROR);
606 	fctx->state = imap_state_select2;
607 	return (FETCH_BLOCK);
608 }
609 
610 /* Select state 2. Wait for continuation and send folder name. */
611 int
imap_state_select2(struct account * a,struct fetch_ctx * fctx)612 imap_state_select2(struct account *a, struct fetch_ctx *fctx)
613 {
614 	struct fetch_imap_data	*data = a->data;
615 	char			*line;
616 
617 	if (imap_getln(a, fctx, IMAP_CONTINUE, &line) != 0)
618 		return (FETCH_ERROR);
619 	if (line == NULL)
620 		return (FETCH_BLOCK);
621 
622 	if (imap_putln(a, "%s", ARRAY_ITEM(data->folders, data->folder)) != 0)
623 		return (FETCH_ERROR);
624 	fctx->state = imap_state_select3;
625 	return (FETCH_BLOCK);
626 }
627 
628 /* Select state 3. Hold until select returns message count. */
629 int
imap_state_select3(struct account * a,struct fetch_ctx * fctx)630 imap_state_select3(struct account *a, struct fetch_ctx *fctx)
631 {
632 	struct fetch_imap_data	*data = a->data;
633 	char			*line;
634 
635 	for (;;) {
636 		if (imap_getln(a, fctx, IMAP_UNTAGGED, &line) != 0)
637 			return (FETCH_ERROR);
638 		if (line == NULL)
639 			return (FETCH_BLOCK);
640 
641 		if (sscanf(line, "* %u EXISTS", &data->total) == 1)
642 			break;
643 	}
644 
645 	fctx->state = imap_state_select4;
646 	return (FETCH_AGAIN);
647 }
648 
649 /* Select state 4. Hold until select completes. */
650 int
imap_state_select4(struct account * a,struct fetch_ctx * fctx)651 imap_state_select4(struct account *a, struct fetch_ctx *fctx)
652 {
653 	struct fetch_imap_data	*data = a->data;
654 	char			*line;
655 
656 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
657 		return (FETCH_ERROR);
658 	if (line == NULL)
659 		return (FETCH_BLOCK);
660 	if (!imap_okay(line))
661 		return (imap_bad(a, line));
662 
663 	/* If no mails, stop early. */
664 	if (data->total == 0) {
665 		if (imap_putln(a, "%u CLOSE", ++data->tag) != 0)
666 			return (FETCH_ERROR);
667 		fctx->state = imap_state_close;
668 		return (FETCH_BLOCK);
669 	}
670 
671 	fctx->state = imap_state_search1;
672 	return (FETCH_AGAIN);
673 }
674 
675 /* Search state 1. Request list of mail required. */
676 int
imap_state_search1(struct account * a,struct fetch_ctx * fctx)677 imap_state_search1(struct account *a, struct fetch_ctx *fctx)
678 {
679 	struct fetch_imap_data	*data = a->data;
680 
681 	/* Search for a list of the mail UIDs to fetch. */
682 	switch (data->only) {
683 	case FETCH_ONLY_NEW:
684 		if (imap_putln(a, "%u UID SEARCH UNSEEN", ++data->tag) != 0)
685 			return (FETCH_ERROR);
686 		break;
687 	case FETCH_ONLY_OLD:
688 		if (imap_putln(a, "%u UID SEARCH SEEN", ++data->tag) != 0)
689 			return (FETCH_ERROR);
690 		break;
691 	default:
692 		if (imap_putln(a, "%u UID SEARCH ALL", ++data->tag) != 0)
693 			return (FETCH_ERROR);
694 		break;
695 	}
696 
697 	fctx->state = imap_state_search2;
698 	return (FETCH_BLOCK);
699 }
700 
701 /* Search state 2. */
702 int
imap_state_search2(struct account * a,struct fetch_ctx * fctx)703 imap_state_search2(struct account *a, struct fetch_ctx *fctx)
704 {
705 	struct fetch_imap_data	*data = a->data;
706 	char			*line, *ptr;
707 	u_int			 uid;
708 
709 	if (imap_getln(a, fctx, IMAP_UNTAGGED, &line) != 0)
710 		return (FETCH_ERROR);
711 	if (line == NULL)
712 		return (FETCH_BLOCK);
713 
714 	/* Skip the header. */
715 	if (strncasecmp(line, "* SEARCH", 8) != 0)
716 		return (imap_bad(a, line));
717 	line += 8;
718 
719 	/* Read each UID and save it. */
720 	do {
721 		while (isspace((u_char) *line))
722 			line++;
723 		ptr = strchr(line, ' ');
724 		if (ptr == NULL)
725 			ptr = strchr(line, '\0');
726 		if (ptr == line)
727 			break;
728 
729 		if (sscanf(line, "%u", &uid) != 1)
730 			return (imap_bad(a, line));
731 		ARRAY_ADD(&data->wanted, uid);
732 		log_debug3("%s: fetching UID: %u", a->name, uid);
733 
734 		line = ptr;
735 	} while (*line == ' ');
736 
737 	fctx->state = imap_state_search3;
738 	return (FETCH_AGAIN);
739 }
740 
741 /* Search state 3. */
742 int
imap_state_search3(struct account * a,struct fetch_ctx * fctx)743 imap_state_search3(struct account *a, struct fetch_ctx *fctx)
744 {
745 	struct fetch_imap_data	*data = a->data;
746 	char			*line;
747 
748 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
749 		return (FETCH_ERROR);
750 	if (line == NULL)
751 		return (FETCH_BLOCK);
752 	if (!imap_okay(line))
753 		return (imap_bad(a, line));
754 
755 	/* Save the total. */
756 	data->total = ARRAY_LENGTH(&data->wanted);
757 
758 	/* Update grand total. */
759 	data->folders_total += data->total;
760 
761 	/* If no mails, or polling, stop here. */
762 	if (data->total == 0 || fctx->flags & FETCH_POLL) {
763 		if (imap_putln(a, "%u CLOSE", ++data->tag) != 0)
764 			return (FETCH_ERROR);
765 		fctx->state = imap_state_close;
766 		return (FETCH_BLOCK);
767 	}
768 
769 	fctx->state = imap_state_next;
770 	return (FETCH_AGAIN);
771 }
772 
773 /*
774  * Next state. Get next mail. This is also the idle state when completed, so
775  * check for finished mail, exiting, and so on.
776  */
777 int
imap_state_next(struct account * a,struct fetch_ctx * fctx)778 imap_state_next(struct account *a, struct fetch_ctx *fctx)
779 {
780 	struct fetch_imap_data	*data = a->data;
781 
782 	/* Handle dropped and kept mail. */
783 	if (!ARRAY_EMPTY(&data->dropped)) {
784 		if (imap_putln(a, "%u UID STORE %u +FLAGS.SILENT (\\Deleted)",
785 		    ++data->tag, ARRAY_FIRST(&data->dropped)) != 0)
786 			return (FETCH_ERROR);
787 		ARRAY_REMOVE(&data->dropped, 0);
788 		fctx->state = imap_state_commit;
789 		return (FETCH_BLOCK);
790 	}
791 	if (!ARRAY_EMPTY(&data->kept)) {
792 		/*
793 		 * GMail is broken and does not set the \Seen flag after mail
794 		 * is fetched, so set it explicitly for kept mail.
795 		 */
796 		if (imap_putln(a, "%u UID STORE %u +FLAGS.SILENT (\\Seen)",
797 		    ++data->tag, ARRAY_FIRST(&data->kept)) != 0)
798 			return (FETCH_ERROR);
799 		ARRAY_REMOVE(&data->kept, 0);
800 		fctx->state = imap_state_commit;
801 		return (FETCH_BLOCK);
802 	}
803 
804 	/* Need to purge, switch to purge state. */
805 	if (fctx->flags & FETCH_PURGE) {
806 		/*
807 		 * If can't purge now, loop through this state until there is
808 		 * no mail on the dropped queue and FETCH_EMPTY is set. Can't
809 		 * have a seperate state to loop through without returning
810 		 * here: mail could potentially be added to the dropped list
811 		 * while in that state.
812 		 */
813 		if (fctx->flags & FETCH_EMPTY) {
814 			fctx->flags &= ~FETCH_PURGE;
815 
816 			if (imap_putln(a, "%u EXPUNGE", ++data->tag) != 0)
817 				return (FETCH_ERROR);
818 			fctx->state = imap_state_expunge;
819 			return (FETCH_BLOCK);
820 		}
821 
822 		/*
823 		 * Must be waiting for delivery, so permit blocking even though
824 		 * we (fetch) aren't waiting for any data.
825 		 */
826 		return (FETCH_BLOCK);
827 	}
828 
829 	/* If last mail, wait for everything to be committed then close down. */
830 	if (ARRAY_EMPTY(&data->wanted)) {
831 		if (data->committed != data->total)
832 			return (FETCH_BLOCK);
833 		if (imap_putln(a, "%u CLOSE", ++data->tag) != 0)
834 			return (FETCH_ERROR);
835 		fctx->state = imap_state_close;
836 		return (FETCH_BLOCK);
837 	}
838 
839 	/* Fetch the next mail. */
840 	if (imap_putln(a, "%u "
841 	    "UID FETCH %u BODY[]",++data->tag, ARRAY_FIRST(&data->wanted)) != 0)
842 		return (FETCH_ERROR);
843 	fctx->state = imap_state_body;
844 	return (FETCH_BLOCK);
845 }
846 
847 /* Body state. */
848 int
imap_state_body(struct account * a,struct fetch_ctx * fctx)849 imap_state_body(struct account *a, struct fetch_ctx *fctx)
850 {
851 	struct fetch_imap_data	*data = a->data;
852 	struct mail		*m = fctx->mail;
853 	struct fetch_imap_mail	*aux;
854 	char			*line, *ptr;
855 	u_int			 n;
856 
857 	if (imap_getln(a, fctx, IMAP_UNTAGGED, &line) != 0)
858 		return (FETCH_ERROR);
859 	if (line == NULL)
860 		return (FETCH_BLOCK);
861 
862 	if (sscanf(line, "* %u FETCH (", &n) != 1)
863 		return (imap_invalid(a, line));
864 	if ((ptr = strstr(line, "BODY[] {")) == NULL)
865 		return (imap_invalid(a, line));
866 
867 	if (sscanf(ptr, "BODY[] {%zu}", &data->size) != 1)
868 		return (imap_invalid(a, line));
869 	data->lines = 0;
870 
871 	/* Fill in local data. */
872 	aux = xcalloc(1, sizeof *aux);
873 	aux->uid = ARRAY_FIRST(&data->wanted);
874 	m->auxdata = aux;
875 	m->auxfree = imap_free;
876 	ARRAY_REMOVE(&data->wanted, 0);
877 
878 	/* Open the mail. */
879 	if (mail_open(m, data->size) != 0) {
880 		log_warnx("%s: failed to create mail", a->name);
881 		return (FETCH_ERROR);
882 	}
883 	m->size = 0;
884 
885 	/* Tag mail. */
886 	default_tags(&m->tags, data->src);
887 	if (data->server.host != NULL) {
888 		add_tag(&m->tags, "server", "%s", data->server.host);
889 		add_tag(&m->tags, "port", "%s", data->server.port);
890 	}
891 	add_tag(&m->tags, "server_uid", "%u", aux->uid);
892 	add_tag(&m->tags,
893 	    "folder", "%s", ARRAY_ITEM(data->folders, data->folder));
894 
895 	/* If we already know the mail is oversize, start off flushing it. */
896 	data->flushing = data->size > conf.max_size;
897 
898 	fctx->state = imap_state_line;
899 	return (FETCH_AGAIN);
900 }
901 
902 /* Line state. */
903 int
imap_state_line(struct account * a,struct fetch_ctx * fctx)904 imap_state_line(struct account *a, struct fetch_ctx *fctx)
905 {
906 	struct fetch_imap_data	*data = a->data;
907 	struct mail		*m = fctx->mail;
908 	char			*line;
909 	size_t			 used, size, left;
910 
911 	for (;;) {
912 		if (imap_getln(a, fctx, IMAP_RAW, &line) != 0)
913 			return (FETCH_ERROR);
914 		if (line == NULL)
915 			return (FETCH_BLOCK);
916 
917 		if (data->flushing)
918 			continue;
919 
920 		/* Check if this line would exceed the expected size. */
921 		used = m->size + data->lines;
922 		size = strlen(line);
923 		if (used + size + 2 > data->size)
924 			break;
925 
926 		if (append_line(m, line, size) != 0) {
927 			log_warnx("%s: failed to resize mail", a->name);
928 			return (FETCH_ERROR);
929 		}
930 		data->lines++;
931 	}
932 
933 	/*
934 	 * Calculate the number of bytes still needed. The current line must
935 	 * include at least that much data. Some servers include UID or FLAGS
936 	 * after the message: we don't care about these so just ignore them and
937 	 * make sure there is a terminating ).
938 	 */
939 	left = data->size - used;
940 	if (line[size - 1] != ')' && size <= left)
941 		return (imap_invalid(a, line));
942 
943 	/* If there was data left, add it as a new line without trailing \n. */
944 	if (left > 0) {
945 		if (append_line(m, line, left) != 0) {
946 			log_warnx("%s: failed to resize mail", a->name);
947 			return (FETCH_ERROR);
948 		}
949 		data->lines++;
950 
951 		/* Wipe out the trailing \n. */
952 		m->size--;
953 	}
954 
955 	fctx->state = imap_state_mail;
956 	return (FETCH_AGAIN);
957 }
958 
959 /* Mail state. */
960 int
imap_state_mail(struct account * a,struct fetch_ctx * fctx)961 imap_state_mail(struct account *a, struct fetch_ctx *fctx)
962 {
963 	struct fetch_imap_data	*data = a->data;
964 	char	*line;
965 
966 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
967 		return (FETCH_ERROR);
968 	if (line == NULL)
969 		return (FETCH_BLOCK);
970 	if (!imap_okay(line))
971 		return (imap_bad(a, line));
972 
973 	if (data->capa & IMAP_CAPA_GMEXT) {
974 		fctx->state = imap_state_gmext_start;
975 		return (FETCH_AGAIN);
976 	}
977 
978 	fctx->state = imap_state_next;
979 	return (FETCH_MAIL);
980 }
981 
982 /* GMail extensions start state. */
983 int
imap_state_gmext_start(struct account * a,struct fetch_ctx * fctx)984 imap_state_gmext_start(struct account *a, struct fetch_ctx *fctx)
985 {
986 	struct fetch_imap_data	*data = a->data;
987 	struct mail		*m = fctx->mail;
988 	struct fetch_imap_mail	*aux = m->auxdata;
989 
990 	if (imap_putln(a, "%u FETCH %u (X-GM-MSGID X-GM-THRID X-GM-LABELS)",
991 	    ++data->tag, aux->uid) != 0)
992 		return (FETCH_ERROR);
993 
994 	fctx->state = imap_state_gmext_body;
995 	return (FETCH_AGAIN);
996 }
997 
998 /* GMail extensions body state. */
999 int
imap_state_gmext_body(struct account * a,struct fetch_ctx * fctx)1000 imap_state_gmext_body(struct account *a, struct fetch_ctx *fctx)
1001 {
1002 	struct fetch_imap_data	*data = a->data;
1003 	struct mail		*m = fctx->mail;
1004 	char			*line, *lb;
1005 	int     	         tag;
1006 	u_int			 n;
1007 	uint64_t		 thrid, msgid;
1008 	size_t			 lblen;
1009 
1010 	for (;;) {
1011 		if (imap_getln(a, fctx, IMAP_RAW, &line) != 0)
1012 			return (FETCH_ERROR);
1013 		if (line == NULL)
1014 			return (FETCH_BLOCK);
1015 		tag = imap_tag(line);
1016 		if (tag == IMAP_TAG_NONE)
1017 			break;
1018 		if (tag == data->tag) {
1019 			fctx->state = imap_state_next;
1020 			return (FETCH_MAIL);
1021 		}
1022 		return (FETCH_ERROR);
1023 	}
1024 
1025 	if (sscanf(line, "* %u FETCH (X-GM-THRID %llu X-GM-MSGID %llu ", &n,
1026 	    &thrid, &msgid) != 3)
1027 		return (imap_invalid(a, line));
1028 	if ((lb = strstr(line, "X-GM-LABELS")) == NULL)
1029 		return (imap_invalid(a, line));
1030 	if ((lb = strchr(lb, '(')) == NULL)
1031 		return (imap_invalid(a, line));
1032 	lb++; /* drop '(' */
1033 	lblen = strlen(lb);
1034 	if (lblen < 2 || lb[lblen - 1] != ')' || lb[lblen - 2] != ')')
1035 		return (imap_invalid(a, line));
1036 	lblen -= 2; /* drop '))' from the end */
1037 
1038 	add_tag(&m->tags, "gmail_msgid", "%llu", msgid);
1039 	add_tag(&m->tags, "gmail_thrid", "%llu", thrid);
1040 	add_tag(&m->tags, "gmail_labels", "%.*s", (int)lblen, lb);
1041 
1042 	fctx->state = imap_state_gmext_done;
1043 	return (FETCH_AGAIN);
1044 }
1045 
1046 /* GMail extensions done state. */
1047 int
imap_state_gmext_done(struct account * a,struct fetch_ctx * fctx)1048 imap_state_gmext_done(struct account *a, struct fetch_ctx *fctx)
1049 {
1050 	char	*line;
1051 
1052 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
1053 		return (FETCH_ERROR);
1054 	if (line == NULL)
1055 		return (FETCH_BLOCK);
1056 	if (!imap_okay(line))
1057 		return (imap_bad(a, line));
1058 
1059 	fctx->state = imap_state_next;
1060 	return (FETCH_MAIL);
1061 }
1062 
1063 /* Commit state. */
1064 int
imap_state_commit(struct account * a,struct fetch_ctx * fctx)1065 imap_state_commit(struct account *a, struct fetch_ctx *fctx)
1066 {
1067 	struct fetch_imap_data	*data = a->data;
1068 	char			*line;
1069 
1070 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
1071 		return (FETCH_ERROR);
1072 	if (line == NULL)
1073 		return (FETCH_BLOCK);
1074 	if (!imap_okay(line))
1075 		return (imap_bad(a, line));
1076 
1077 	data->committed++;
1078 
1079 	fctx->state = imap_state_next;
1080 	return (FETCH_AGAIN);
1081 }
1082 
1083 /* Expunge state. */
1084 int
imap_state_expunge(struct account * a,struct fetch_ctx * fctx)1085 imap_state_expunge(struct account *a, struct fetch_ctx *fctx)
1086 {
1087 	char	*line;
1088 
1089 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
1090 		return (FETCH_ERROR);
1091 	if (line == NULL)
1092 		return (FETCH_BLOCK);
1093 	if (!imap_okay(line))
1094 		return (imap_bad(a, line));
1095 
1096 	fctx->state = imap_state_next;
1097 	return (FETCH_AGAIN);
1098 }
1099 
1100 /* Close state. */
1101 int
imap_state_close(struct account * a,struct fetch_ctx * fctx)1102 imap_state_close(struct account *a, struct fetch_ctx *fctx)
1103 {
1104 	struct fetch_imap_data	*data = a->data;
1105 	char			*line;
1106 
1107 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
1108 		return (FETCH_ERROR);
1109 	if (line == NULL)
1110 		return (FETCH_BLOCK);
1111 	if (!imap_okay(line))
1112 		return (imap_bad(a, line));
1113 
1114 	data->folder++;
1115 	if (data->folder != ARRAY_LENGTH(data->folders)) {
1116 		ARRAY_FREE(&data->wanted);
1117 		data->committed = 0;
1118 
1119 		fctx->state = imap_state_select1;
1120 		return (FETCH_AGAIN);
1121 	}
1122 
1123 	if (imap_putln(a, "%u LOGOUT", ++data->tag) != 0)
1124 		return (FETCH_ERROR);
1125 	fctx->state = imap_state_quit;
1126 	return (FETCH_BLOCK);
1127 }
1128 
1129 /* Quit state. */
1130 int
imap_state_quit(struct account * a,struct fetch_ctx * fctx)1131 imap_state_quit(struct account *a, struct fetch_ctx *fctx)
1132 {
1133 	char	*line;
1134 
1135 	if (imap_getln(a, fctx, IMAP_TAGGED, &line) != 0)
1136 		return (FETCH_ERROR);
1137 	if (line == NULL)
1138 		return (FETCH_BLOCK);
1139 	if (!imap_okay(line))
1140 		return (imap_bad(a, line));
1141 
1142 	imap_abort(a);
1143 	return (FETCH_EXIT);
1144 }
1145