rohrpost

A commandline mail client to change the world as we see it.
git clone git://r-36.net/rohrpost
Log | Files | Refs | README | LICENSE

imap.c (23673B)


      1 /*
      2  * Copy me if you can.
      3  * by 20h
      4  */
      5 
      6 #include <unistd.h>
      7 #include <stdlib.h>
      8 #include <stdio.h>
      9 #include <string.h>
     10 #include <strings.h>
     11 #include <stdarg.h>
     12 #include <time.h>
     13 #include <ctype.h>
     14 
     15 #include "ind.h"
     16 #include "llist.h"
     17 #include "net.h"
     18 #include "imap.h"
     19 #include "mark.h"
     20 #include "parser.h"
     21 #include "inc.h"
     22 
     23 imap_t *
     24 imap_new(char *netspec, char *user, char *pass)
     25 {
     26 	imap_t *imap;
     27 
     28 	imap = mallocz(sizeof(imap_t), 2);
     29 	imap->netspec = memdups(netspec);
     30 	imap->user = memdups(user);
     31 	imap->pass = memdups(pass);
     32 	imap->starttls = 1;
     33 
     34 	return imap;
     35 }
     36 
     37 void
     38 imap_free(imap_t *imap)
     39 {
     40 	if (imap->netspec != NULL)
     41 		free(imap->netspec);
     42 	if (imap->user != NULL)
     43 		free(imap->user);
     44 	if (imap->pass != NULL)
     45 		free(imap->pass);
     46 	if (imap->imaperror != NULL)
     47 		free(imap->imaperror);
     48 	if (imap->selected != NULL)
     49 		free(imap->selected);
     50 
     51 	if (imap->fd != NULL)
     52 		net_free(imap->fd);
     53 	if (imap->caps != NULL)
     54 		llist_free(imap->caps);
     55 	if (imap->parser != NULL)
     56 		parser_free(imap->parser);
     57 	free(imap);
     58 }
     59 
     60 void
     61 imap_die(imap_t *imap, char *fmt, ...)
     62 {
     63 	va_list fmtargs;
     64 
     65 	va_start(fmtargs, fmt);
     66 	vfprintf(stderr, fmt, fmtargs);
     67 	va_end(fmtargs);
     68 
     69 	if (imap->imaperror != NULL) {
     70 		fprintf(stderr, ": %s\n", imap->imaperror);
     71 		free(imap->imaperror);
     72 	} else
     73 		fprintf(stderr, "\n");
     74 
     75 	exit(1);
     76 }
     77 
     78 int
     79 imap_isstratom(char *key)
     80 {
     81 	return (!strcmp(key, "atom") | !strcmp(key, "string"));
     82 }
     83 
     84 llist_t *
     85 imap_llist2ids(char *cfgn, char *mailbox, llist_t *elems)
     86 {
     87 	llist_t *ids, *result, *ret;
     88 	llistelem_t *elem;
     89 	mark_t *marks = NULL;
     90 	char *split0, *split1, *rstr;
     91 	int last, first, cur, a, b, c, d;
     92 
     93 	ret = NULL;
     94 	first = 1;
     95 
     96 	/*
     97 	 * If no configuration is given, only handle ranges.
     98 	 * Is used in part.c.
     99 	 */
    100 	if (mailbox != NULL) {
    101 		rstr = inc_getstr(cfgn, mailbox, "messages");
    102 		if (rstr == NULL)
    103 			return NULL;
    104 		last = atoi(rstr);
    105 		free(rstr);
    106 
    107 		marks = mark_init(cfgn, mailbox);
    108 		if (marks == NULL)
    109 			return NULL;
    110 
    111 		rstr = mark_getstr(marks, "cur");
    112 		if (rstr == NULL)
    113 			cur = first;
    114 		else
    115 			cur = atoi(rstr);
    116 	}
    117 
    118 	ids = llist_new();
    119 	forllist(elems, elem) {
    120 		if (mailbox != NULL) {
    121 			result = mark_getlist(marks, elem->key);
    122 			if (result != NULL) {
    123 				ids = llist_rawlistadd(ids, result);
    124 				llist_bfree(result);
    125 				continue;
    126 			}
    127 
    128 			if (!strcmp(elem->key, "c")) {
    129 				llist_addraw(ids, smprintf("%d", cur), NULL, 0);
    130 				continue;
    131 			}
    132 
    133 			if (elem->key[0] == 'c') {
    134 				b = atoi(&elem->key[1]);
    135 				if (b < 0) {
    136 					if (cur + b < 1)
    137 						b = 1 - cur;
    138 
    139 					result = llist_genrange(cur + b, cur + 1, 1);
    140 				} else {
    141 					if (cur + b + 1 > last)
    142 						b = last - cur - 1;
    143 
    144 					result = llist_genrange(cur, cur + b + 1, 1);
    145 				}
    146 				if (result == NULL)
    147 					continue;
    148 				llist_rawlistadd(ids, result);
    149 				llist_bfree(result);
    150 				continue;
    151 			}
    152 		}
    153 
    154 		split0 = strchr(elem->key, ':');
    155 		if (split0 == NULL) {
    156 			if (mailbox != NULL) {
    157 				if (elem->key[0] == '-') {
    158 					if (!isdigit(elem->key[1]))
    159 						continue;
    160 				} else if (!isdigit(elem->key[0])) {
    161 					continue;
    162 				}
    163 				b = atoi(elem->key);
    164 				if (b < 1)
    165 					b = last + b + 1;
    166 				if (b < 1)
    167 					b = 1;
    168 				if (b > last)
    169 					b = last;
    170 
    171 				llist_addraw(ids, smprintf("%d", b), NULL, 0);
    172 			} else {
    173 				llist_addraw(ids, smprintf("%s", elem->key),
    174 					NULL, 0);
    175 			}
    176 			continue;
    177 		} else {
    178 			c = 1;
    179 			split1 = strchr(&split0[1], ':');
    180 			if (split1 != NULL)
    181 				c = atoi(&split1[1]);
    182 
    183 			if (elem->key[0] == ':') {
    184 				a = first;
    185 			} else {
    186 				a = atoi(elem->key);
    187 			}
    188 			if (split0[1] == ':' || split0[1] == '\0') {
    189 				if (cfgn != NULL) {
    190 					b = last;
    191 				} else {
    192 					b = 0;
    193 				}
    194 			} else {
    195 				b = atoi(&split0[1]);
    196 			}
    197 
    198 			if (mailbox != NULL) {
    199 				if (a < 0)
    200 					a = last + a + 1;
    201 				if (b < 0)
    202 					b = last + b + 1;
    203 			}
    204 			if (a > b) {
    205 				d = a;
    206 				a = b;
    207 				b = d;
    208 			}
    209 			if (cfgn != NULL) {
    210 				if (b + 1 > last)
    211 					b = last;
    212 			}
    213 
    214 			if (a == 0 || b == 0) {
    215 				llist_addraw(ids, smprintf("%s", elem->key),
    216 					NULL, 0);
    217 			} else {
    218 				result = llist_genrange(a, b + 1, c);
    219 				if (result == NULL)
    220 					continue;
    221 				llist_rawlistadd(ids, result);
    222 				llist_bfree(result);
    223 			}
    224 		}
    225 		continue;
    226 	}
    227 	if (ids->len < 1) {
    228 		llist_free(ids);
    229 		goto badmarkending;
    230 	}
    231 
    232 	//llist_intsort(ids);
    233 	ret = ids;
    234 badmarkending:
    235 	if (marks != NULL)
    236 		mark_free(marks);
    237 	return ret;
    238 }
    239 
    240 llist_t *
    241 imap_str2ids(char *cfgn, char *mailbox, char *str)
    242 {
    243 	llist_t *ids, *ssplit;
    244 
    245 	ssplit = llist_splitstr(str, " ,");
    246 	if (ssplit == NULL)
    247 		return NULL;
    248 	ids = imap_llist2ids(cfgn, mailbox, ssplit);
    249 	llist_free(ssplit);
    250 
    251 	return ids;
    252 }
    253 
    254 llist_t *
    255 imap_argv2ids(char *cfgn, char *mailbox, int argc, char *argv[])
    256 {
    257 	llist_t *allist, *llist, *ids, *nids;
    258 	llistelem_t *argelem;
    259 
    260 	allist = llist_splitargv(argc, argv);
    261 	if (allist == NULL)
    262 		return NULL;
    263 
    264 	llist = llist_new();
    265 	forllist(allist, argelem) {
    266 		if (argelem->key == NULL)
    267 			continue;
    268 		nids = llist_splitstr(argelem->key, " ,");
    269 		if (nids != NULL) {
    270 			if (nids->len > 0)
    271 				llist_listadd(llist, nids);
    272 			llist_free(nids);
    273 		}
    274 	}
    275 	llist_free(allist);
    276 
    277 	if (llist->len > 0) {
    278 		ids = imap_llist2ids(cfgn, mailbox, llist);
    279 	} else {
    280 		ids = NULL;
    281 	}
    282 	llist_free(llist);
    283 
    284 	return ids;
    285 }
    286 
    287 char *
    288 imap_ids2str(llist_t *ids)
    289 {
    290 	int *ida, i, nb, nc;
    291 	llistelem_t *elem;
    292 	llist_t *seqs;
    293 	char *ret, *el;
    294 
    295 	if (ids->len < 1)
    296 		return NULL;
    297 
    298 	ida = mallocz(sizeof(int) * ids->len, 2);
    299 	i = 0;
    300 	forllist(ids, elem)
    301 		ida[i++] = atoi(elem->key);
    302 	qsort(ida, ids->len, sizeof(int), intcmp);
    303 
    304 	seqs = llist_new();
    305 	for (i = 1, nb = ida[0], nc = 0; i < ids->len; i++) {
    306 		if (ida[i] == nb + nc + 1) {
    307 			nc += 1;
    308 			continue;
    309 		}
    310 		if (ida[i] == nb + nc)
    311 			continue;
    312 
    313 		if (nc > 0) {
    314 			el = smprintf("%d:%d", nb, nb+nc);
    315 		} else {
    316 			el = smprintf("%d", nb);
    317 		}
    318 		llist_addraw(seqs, el, NULL, 0);
    319 		nb = ida[i];
    320 		nc = 0;
    321 	}
    322 
    323 	if (nc > 0) {
    324 		el = smprintf("%d:%d", nb, nb+nc);
    325 	} else {
    326 		el = smprintf("%d", nb);
    327 	}
    328 	free(ida);
    329 	llist_addraw(seqs, el, NULL, 0);
    330 
    331 	ret = llist_joinstr(seqs, ",");
    332 	llist_free(seqs);
    333 
    334 	return ret;
    335 }
    336 
    337 void
    338 imap_cmd(imap_t *imap, char *cmd, ...)
    339 {
    340 	va_list ap;
    341 	char *req, *breq, *tag, *arg;
    342 
    343 	req = smprintf("%s", cmd);
    344 	tag = smprintf("a%.3d", ++imap->msgid);
    345 
    346 	va_start(ap, cmd);
    347 	for (arg = va_arg(ap, char *); arg; arg = va_arg(ap, char *)) {
    348 		breq = smprintf("%s %s", req, arg);
    349 		free(req);
    350 		req = breq;
    351 	}
    352 	va_end(ap);
    353 
    354 	//printf("%s %s\n", tag, req);
    355 	net_printf(imap->fd, "%s %s\r\n", tag, req);
    356 	free(tag);
    357 	free(req);
    358 }
    359 
    360 void
    361 imap_simplecmd(imap_t *imap, char *cmd)
    362 {
    363 	imap_cmd(imap, cmd, NULL);
    364 }
    365 
    366 int
    367 imap_parseline(imap_t *imap, llist_t **ret)
    368 {
    369 	llist_t *lineres;
    370 	llistelem_t *result;
    371 	char bc, *line;
    372 	int len, retval;
    373 
    374 	retval = 1;
    375 
    376 	result = parser_parseimapstruct(imap->parser);
    377 	if (result == NULL)
    378 		return -1;
    379 
    380 	if (strcmp(result->key, "atom")) {
    381 		llistelem_efree(result);
    382 		return -1;
    383 	}
    384 
    385 	bc = ((char *)result->data)[0];
    386 	llistelem_efree(result);
    387 	if (bc != '*') {
    388 		result = parser_parseimapstruct(imap->parser);
    389 		if (!strcmp((char *)result->data, "OK"))
    390 			retval = 0;
    391 		llistelem_efree(result);
    392 
    393 		line = net_gets(imap->fd);
    394 		len = strlen(line);
    395 		if (line[len-1] == '\r' || line[len-1] == '\n')
    396 			line[len-1] = '\0';
    397 		if (imap->imaperror != NULL)
    398 			free(imap->imaperror);
    399 		imap->imaperror = memdups(line);
    400 		free(line);
    401 		return retval;
    402 	}
    403 
    404 	lineres = llist_new();
    405 	for (; (result = parser_parseimapstruct(imap->parser));)
    406 		llist_addelem(lineres, result);
    407 	*ret = lineres;
    408 
    409 	return -1;
    410 }
    411 
    412 int
    413 imap_result(imap_t *imap, llist_t **ret)
    414 {
    415 	int retval;
    416 	llist_t *results, *lineres;
    417 
    418 	results = llist_new();
    419 	retval = 1;
    420 	for (;;) {
    421 		lineres = NULL;
    422 		retval = imap_parseline(imap, &lineres);
    423 		if (retval > -1 || lineres == NULL)
    424 			break;
    425 
    426 		if (lineres->len < 1) {
    427 			llist_efree(lineres);
    428 			continue;
    429 		}
    430 		llist_addraw(results, NULL, lineres, sizeof(lineres));
    431 	}
    432 
    433 	if (results->len < 1)
    434 		llist_efree(results);
    435 	else
    436 		*ret = results;
    437 
    438 	return retval;
    439 }
    440 
    441 int
    442 imap_simpleresult(imap_t *imap)
    443 {
    444 	llist_t *retl;
    445 	int ret;
    446 
    447 	retl = NULL;
    448 	ret = imap_result(imap, &retl);
    449 	if (retl != NULL)
    450 		llist_efree(retl);
    451 
    452 	return ret;
    453 }
    454 
    455 int
    456 imap_capabilityset(imap_t *imap, llist_t *retcaps)
    457 {
    458 	llist_t *caps;
    459 	llistelem_t *elem;
    460 	int status, clen;
    461 	char *capability;
    462 
    463 	enum {
    464 		ISMANGLED = 0x01,
    465 		ISCAPS = 0x02,
    466 		ISSIMPLE = 0x04
    467 	};
    468 
    469 	status = 0;
    470 
    471 	caps = llist_new();
    472 	forllist(retcaps, elem) {
    473 		if (elem->key == NULL)
    474 			continue;
    475 		if (!strcmp(elem->key, "[CAPABILITY")) {
    476 			status |= ISSIMPLE | ISCAPS | ISMANGLED;
    477 			continue;
    478 		}
    479 		if (status & ISSIMPLE) {
    480 			capability = elem->key;
    481 			clen = strlen(capability);
    482 		} else {
    483 			if (strcmp(elem->key, "atom"))
    484 				continue;
    485 			if (elem->data == NULL)
    486 				continue;
    487 			if (!strcmp((char *)elem->data, "CAPABILITY")) {
    488 				status |= ISCAPS;
    489 				continue;
    490 			}
    491 			if (!strcmp((char *)elem->data, "[CAPABILITY")) {
    492 				status |= ISCAPS | ISMANGLED;
    493 				continue;
    494 			}
    495 			capability = (char *)elem->data;
    496 			clen = elem->datalen-1;
    497 		}
    498 		if (!(status & ISCAPS))
    499 			continue;
    500 
    501 		if (status & ISMANGLED) {
    502 			if (capability[clen-1] == ']') {
    503 				capability[clen-1] = '\0';
    504 				llist_add(caps, capability, NULL, 0);
    505 				break;
    506 			}
    507 		}
    508 
    509 		llist_add(caps, capability, NULL, 0);
    510 	}
    511 
    512 	if (caps->len < 1) {
    513 		llist_free(caps);
    514 		return 1;
    515 	}
    516 
    517 	if (imap->caps != NULL)
    518 		llist_free(imap->caps);
    519 	imap->caps = caps;
    520 
    521 	return 0;
    522 }
    523 
    524 int
    525 imap_capability(imap_t *imap)
    526 {
    527 	llist_t *retcaps;
    528 	llistelem_t *retcap;
    529 	int retval;
    530 
    531 	imap_simplecmd(imap, "CAPABILITY");
    532 	retcaps = NULL;
    533 	if (imap_result(imap, &retcaps)) {
    534 		if (retcaps != NULL)
    535 			llist_efree(retcaps);
    536 		return 1;
    537 	}
    538 	if (retcaps == NULL)
    539 		return 1;
    540 
    541 	if (!isstructlist(retcaps)) {
    542 		llist_efree(retcaps);
    543 		return 1;
    544 	}
    545 
    546 	forllist(retcaps, retcap) {
    547 		retval = imap_capabilityset(imap,
    548 				(llist_t *)retcaps->first->data);
    549 		if (!retval)
    550 			break;
    551 	}
    552 	llist_efree(retcaps);
    553 
    554 	return retval;
    555 }
    556 
    557 /*
    558  * OK [CAPABILITY ...]
    559  */
    560 int
    561 imap_capabilityresult(imap_t *imap)
    562 {
    563 	llist_t *caps;
    564 	int retval;
    565 
    566 	retval = imap_simpleresult(imap);
    567 	if (!retval) {
    568 	        if (imap->imaperror != NULL &&
    569 				!strncmp(imap->imaperror, "[CAPABILITY", 11)) {
    570 
    571 			caps = llist_splitstr(imap->imaperror, " ");
    572 			retval = imap_capabilityset(imap, caps);
    573 			llist_free(caps);
    574 
    575 			if (!retval)
    576 				return retval;
    577 		}
    578 		if (imap_capability(imap))
    579 			return 1;
    580 	} else {
    581 		return 1;
    582 	}
    583 
    584 	return 0;
    585 }
    586 
    587 /*
    588  * * [CAPABILITY ...]
    589  */
    590 int
    591 imap_spuriouscapability(imap_t *imap)
    592 {
    593 	int retval;
    594 	llist_t *lineres;
    595 
    596 	lineres = NULL;
    597 	retval = imap_parseline(imap, &lineres);
    598 	if (retval == -1 && lineres != NULL) {
    599 		retval = imap_capabilityset(imap, lineres);
    600 		if (!retval) {
    601 			llist_efree(lineres);
    602 			return 0;
    603 		}
    604 	}
    605 	if (lineres != NULL)
    606 		llist_efree(lineres);
    607 
    608 	return 1;
    609 }
    610 
    611 int
    612 imap_connect(imap_t *imap)
    613 {
    614 	imap->fd = net_new(imap->netspec);
    615 	if (imap->fd == NULL)
    616 		return 1;
    617 
    618 	if (imap->fd->options && strstr(imap->fd->options, "nostarttls"))
    619 		imap->starttls = 0;
    620 
    621 	if (net_connect(imap->fd)) {
    622 		net_free(imap->fd);
    623 		imap->fd = NULL;
    624 		return 1;
    625 	}
    626 	if (imap->parser != NULL)
    627 		parser_free(imap->parser);
    628 	imap->parser = parser_new("net", imap->fd);
    629 
    630 	if (imap_spuriouscapability(imap)) {
    631 		if (imap_capability(imap))
    632 			return 1;
    633 	}
    634 
    635 	return 0;
    636 }
    637 
    638 int
    639 imap_closefolder(imap_t *imap)
    640 {
    641 	int rclos;
    642 
    643 	imap_simplecmd(imap, "CLOSE");
    644 	rclos = imap_simpleresult(imap);
    645 
    646 	if (!rclos) {
    647 		if (imap->selected != NULL) {
    648 			free(imap->selected);
    649 			imap->selected = NULL;
    650 		}
    651 	}
    652 
    653 	return rclos;
    654 }
    655 
    656 void
    657 imap_close(imap_t *imap)
    658 {
    659 	if (imap->selected != NULL)
    660 		imap_closefolder(imap);
    661 
    662 	net_close(imap->fd);
    663 	if (imap->parser != NULL) {
    664 		parser_free(imap->parser);
    665 		imap->parser = NULL;
    666 	}
    667 }
    668 
    669 int
    670 imap_starttls(imap_t *imap)
    671 {
    672 	imap_simplecmd(imap, "STARTTLS");
    673 	if (imap_simpleresult(imap))
    674 		return 1;
    675 	if (net_addssl(imap->fd))
    676 		return 1;
    677 
    678 	if (imap_capability(imap))
    679 		return 1;
    680 
    681 	return 0;
    682 }
    683 
    684 int
    685 imap_authenticate(imap_t *imap)
    686 {
    687 	llistelem_t *result;
    688 	char *authstr;
    689 
    690 	result = llist_get(imap->caps, "AUTH=PLAIN");
    691 	if (result == NULL)
    692 		return 1;
    693 
    694 	authstr = parser_encodeplainlogin(imap->user, imap->pass);
    695 	net_printf(imap->fd, "a%.3d AUTHENTICATE PLAIN %s\r\n",
    696 			++imap->msgid, authstr);
    697 	free(authstr);
    698 
    699 	return imap_capabilityresult(imap);
    700 }
    701 
    702 int
    703 imap_init(imap_t *imap)
    704 {
    705 	llistelem_t *result;
    706 
    707 	if (imap_connect(imap))
    708 		return 1;
    709 
    710 	result = llist_get(imap->caps, "STARTTLS");
    711 	if (result != NULL && imap->starttls) {
    712 		if (imap_starttls(imap))
    713 			return 1;
    714 	}
    715 
    716 	result = llist_get(imap->caps, "LOGINDISABLED");
    717 	if (result != NULL)
    718 		return 1;
    719 	if (imap_authenticate(imap))
    720 		return 1;
    721 
    722 	return 0;
    723 }
    724 
    725 int
    726 imap_append(imap_t *imap, char *mb, llist_t *flags, char *tdate, char *msg)
    727 {
    728 	char *flagcon, *flagstr, *msge;
    729 
    730 	flagstr = NULL;
    731 
    732 	if (flags != NULL) {
    733 		flagcon = llist_joinstr(flags, " ");
    734 		flagstr = smprintf("(%s)", flagcon);
    735 		free(flagcon);
    736 	}
    737 
    738 	msge = parser_encodestring(msg);
    739 	if (tdate != NULL && flagstr != NULL) {
    740 		imap_cmd(imap, "APPEND", mb, flagstr, tdate, msge, NULL);
    741 	} else if (tdate != NULL && flagstr == NULL) {
    742 		imap_cmd(imap, "APPEND", mb, tdate, msge, NULL);
    743 	} else if (tdate == NULL && flagstr != NULL) {
    744 		imap_cmd(imap, "APPEND", mb, flagstr, msge, NULL);
    745 	} else {
    746 		imap_cmd(imap, "APPEND", mb, msge, NULL);
    747 	}
    748 	free(msge);
    749 
    750 	if (flagstr != NULL)
    751 		free(flagstr);
    752 
    753 	return imap_simpleresult(imap);
    754 }
    755 
    756 int
    757 imap_noop(imap_t *imap)
    758 {
    759 	imap_simplecmd(imap, "NOOP");
    760 	return imap_simpleresult(imap);
    761 }
    762 
    763 int
    764 imap_logout(imap_t *imap)
    765 {
    766 	imap_simplecmd(imap, "LOGOUT");
    767 	return imap_simpleresult(imap);
    768 }
    769 
    770 int
    771 imap_expunge(imap_t *imap)
    772 {
    773 	imap_simplecmd(imap, "EXPUNGE");
    774 	return imap_simpleresult(imap);;
    775 }
    776 
    777 int
    778 imap_copy(imap_t *imap, llist_t *ids, char *tomb)
    779 {
    780 	char *idstr;
    781 
    782 	idstr = imap_ids2str(ids);
    783 	imap_cmd(imap, "COPY", idstr, tomb, NULL);
    784 	free(idstr);
    785 
    786 	return imap_simpleresult(imap);
    787 }
    788 
    789 int
    790 imap_subscribe(imap_t *imap, char *mb)
    791 {
    792 	imap_cmd(imap, "SUBSCRIBE", mb, NULL);
    793 	return imap_simpleresult(imap);
    794 }
    795 
    796 int
    797 imap_unsubscribe(imap_t *imap, char *mb)
    798 {
    799 	imap_cmd(imap, "UNSUBSCRIBE", mb, NULL);
    800 	return imap_simpleresult(imap);
    801 }
    802 
    803 int
    804 imap_createfolder(imap_t *imap, char *mb)
    805 {
    806 	imap_cmd(imap, "CREATE", mb, NULL);
    807 	return imap_simpleresult(imap);
    808 }
    809 
    810 int
    811 imap_deletefolder(imap_t *imap, char *mb)
    812 {
    813 	imap_cmd(imap, "DELETE", mb, NULL);
    814 	return imap_simpleresult(imap);
    815 }
    816 
    817 llist_t *
    818 imap_fetch(imap_t *imap, llist_t *ids, char *req)
    819 {
    820 	char *idstr;
    821 	llist_t *ret;
    822 
    823 	idstr = imap_ids2str(ids);
    824 	imap_cmd(imap, "FETCH", idstr, req, NULL);
    825 	free(idstr);
    826 
    827 	ret = NULL;
    828 	if (imap_result(imap, &ret)) {
    829 		if (ret != NULL)
    830 			llist_efree(ret);
    831 		return NULL;
    832 	}
    833 
    834 	return ret;
    835 }
    836 
    837 llist_t *
    838 imap_fetchprepare(imap_t *imap, llist_t *ids, char *req)
    839 {
    840 	llist_t *res, *resus, *flist, *fflist, **sids;
    841 	llistelem_t *elem, *line, *num, *type, *id;
    842 	int foundone, i;
    843 
    844 	res = imap_fetch(imap, ids, req);
    845 	if (res == NULL)
    846 		return NULL;
    847 	if (!isstructlist(res)) {
    848 		llist_efree(res);
    849 		return NULL;
    850 	}
    851 
    852 	foundone = 0;
    853 	sids = mallocz(sizeof(sids[0]) * ids->len, 2);
    854 	forllist(res, line) {
    855 		num = llist_get((llist_t *)line->data, "number");
    856 		if (num == NULL)
    857 			continue;
    858 
    859 		elem = ((llist_t *)line->data)->last;
    860 		if (elem->data == NULL || elem->key != NULL)
    861 			continue;
    862 		flist = (llist_t *)elem->data;
    863 		if (flist->first == NULL && flist->first->data != NULL)
    864 			continue;
    865 		type = flist->first;
    866 
    867 		fflist = llist_new();
    868 		llist_add(fflist, "id", num->data, num->datalen);
    869 		llist_add(fflist, "type", type->data, type->datalen);
    870 		if (flist->last->key == NULL) {
    871 			llist_addraw(fflist, NULL, flist->last->data,
    872 					sizeof(llist_t *));
    873 			flist->last->data = NULL;
    874 		} else {
    875 			elem = flist->last;
    876 			llist_delelemlinks(flist, elem);
    877 			llist_addelem(fflist, elem);
    878 		}
    879 
    880 		foundone = 1;
    881 		i = 0;
    882 		/*
    883 		 * The server is not returning the messages in the order,
    884 		 * as we may have requested them. It is our task to re-
    885 		 * order them.
    886 		 */
    887 		forllist(ids, id) {
    888 			if (atoi(num->data) == atoi(id->key)) {
    889 				sids[i] = fflist;
    890 				break;
    891 			}
    892 			i++;
    893 		}
    894 		if (i >= ids->len)
    895 			llist_efree(fflist);
    896 	}
    897 	llist_efree(res);
    898 
    899 	if (!foundone) {
    900 		free(sids);
    901 		return NULL;
    902 	}
    903 
    904 	resus = llist_new();
    905 	for (i = 0; i < ids->len; i++) {
    906 		if (sids[i] != NULL)
    907 			llist_addraw(resus, NULL, sids[i], sizeof(sids[i]));
    908 	}
    909 	free(sids);
    910 
    911 	return resus;
    912 }
    913 
    914 llist_t *
    915 imap_fetchbody(imap_t *imap, llist_t *ids)
    916 {
    917 	return imap_fetchprepare(imap, ids, "(BODY)");
    918 }
    919 
    920 llist_t *
    921 imap_fetchheaders(imap_t *imap, llist_t *ids)
    922 {
    923 	return imap_fetchprepare(imap, ids, "(BODY.PEEK[HEADER])");
    924 }
    925 
    926 llist_t *
    927 imap_fetchpart(imap_t *imap, llist_t *ids, char *part)
    928 {
    929 	char *pstr;
    930 	llist_t *res;
    931 
    932 	pstr = smprintf("(BODY[%s])", part);
    933 	res = imap_fetchprepare(imap, ids, pstr);
    934 	free(pstr);
    935 
    936 	return res;
    937 }
    938 
    939 llist_t *
    940 imap_fetchraw(imap_t *imap, llist_t *ids)
    941 {
    942 	return imap_fetchpart(imap, ids, "");
    943 }
    944 
    945 llist_t *
    946 imap_status(imap_t *imap, char *mb)
    947 {
    948 	llist_t *status, *retstru;
    949 	llistelem_t *elem, *subelem, *lelem;
    950 	int i;
    951 
    952 	imap_cmd(imap, "STATUS", mb, "(RECENT MESSAGES UNSEEN UIDNEXT"
    953 			" UIDVALIDITY)", NULL);
    954 	retstru = NULL;
    955 	if (imap_result(imap, &retstru)) {
    956 		if (retstru != NULL)
    957 			llist_efree(retstru);
    958 		return NULL;
    959 	}
    960 	if (retstru == NULL)
    961 		return NULL;
    962 
    963 	if (!isstructlist(retstru)) {
    964 		llist_efree(retstru);
    965 		return NULL;
    966 	}
    967 
    968 	status = llist_new();
    969 	forllist(retstru, subelem) {
    970 		i = 0;
    971 		forllist((llist_t *)subelem->data, elem) {
    972 			switch (i) {
    973 			case 0:
    974 				if (elem->data == NULL)
    975 					goto imapstatusbadending;
    976 				if (strcmp((char *)elem->data, "STATUS"))
    977 					goto imapstatusbadending;
    978 				break;
    979 			case 1:
    980 				if (elem->key == NULL)
    981 					goto imapstatusbadending;
    982 				if (!imap_isstratom(elem->key))
    983 					goto imapstatusbadending;
    984 				llist_add(status, "mb", elem->data,
    985 						elem->datalen);
    986 				break;
    987 			default:
    988 				if (elem->key != NULL || elem->data == NULL)
    989 					goto imapstatusbadending;
    990 
    991 				forllist((llist_t *)elem->data, lelem) {
    992 					if (lelem->data == NULL)
    993 						break;
    994 					if (lelem->next == NULL)
    995 						break;
    996 					if (lelem->next->data == NULL)
    997 						break;
    998 					llist_add(status, (char *)lelem->data,
    999 						(char *)lelem->next->data,
   1000 						lelem->next->datalen);
   1001 					lelem = lelem->next;
   1002 				}
   1003 				break;
   1004 			}
   1005 			i++;
   1006 		}
   1007 	}
   1008 	llist_efree(retstru);
   1009 	if (status->len < 2) {
   1010 		llist_efree(status);
   1011 		return NULL;
   1012 	}
   1013 
   1014 	return status;
   1015 imapstatusbadending:
   1016 	llist_efree(retstru);
   1017 	llist_free(status);
   1018 
   1019 	return NULL;
   1020 }
   1021 
   1022 llist_t *
   1023 imap_listresponse(imap_t *imap, char *cmd)
   1024 {
   1025 	llist_t *folders, *retstru, *slist;
   1026 	llistelem_t *elem;
   1027 
   1028 	imap_cmd(imap, cmd, "\"\"", "*", NULL);
   1029 	retstru = NULL;
   1030 	if (imap_result(imap, &retstru)) {
   1031 		if (retstru != NULL)
   1032 			llist_efree(retstru);
   1033 		return NULL;
   1034 	}
   1035 	if (retstru == NULL)
   1036 		return NULL;
   1037 
   1038 	if (!isstructlist(retstru)) {
   1039 		llist_efree(retstru);
   1040 		return NULL;
   1041 	}
   1042 
   1043 	folders = llist_new();
   1044 	forllist(retstru, elem) {
   1045 		if (elem->key != NULL)
   1046 			continue;
   1047 
   1048 		slist = (llist_t *)elem->data;
   1049 		if (slist->last->key == NULL)
   1050 			continue;
   1051 		if (!imap_isstratom((char *)slist->last->key))
   1052 			continue;
   1053 		if (slist->last->data == NULL)
   1054 			continue;
   1055 		llist_add(folders, (char *)slist->last->data, NULL, 0);
   1056 	}
   1057 	llist_efree(retstru);
   1058 
   1059 	if (folders->len < 1) {
   1060 		llist_free(folders);
   1061 		return NULL;
   1062 	}
   1063 
   1064 	return folders;
   1065 }
   1066 
   1067 llist_t *
   1068 imap_subscribed(imap_t *imap)
   1069 {
   1070 	return imap_listresponse(imap, "LSUB");
   1071 }
   1072 
   1073 llist_t *
   1074 imap_listfolders(imap_t *imap)
   1075 {
   1076 	return imap_listresponse(imap, "LIST");
   1077 }
   1078 
   1079 llist_t *
   1080 imap_statuses(imap_t *imap)
   1081 {
   1082 	llist_t *mbs, *statuses, *status;
   1083 	llistelem_t *mb;
   1084 
   1085 	mbs = imap_subscribed(imap);
   1086 	if (mbs == NULL)
   1087 		return NULL;
   1088 
   1089 	statuses = llist_new();
   1090 	forllist(mbs, mb) {
   1091 		if (mb->key == NULL)
   1092 			continue;
   1093 		status = imap_status(imap, mb->key);
   1094 		if (status == NULL)
   1095 			continue;
   1096 		llist_addraw(statuses, NULL, status, sizeof(status));
   1097 	}
   1098 	llist_efree(mbs);
   1099 	if (statuses->len < 1) {
   1100 		llist_free(statuses);
   1101 		return NULL;
   1102 	}
   1103 
   1104 	return statuses;
   1105 }
   1106 
   1107 int
   1108 imap_renamefolder(imap_t *imap, char *old, char *new)
   1109 {
   1110 	imap_cmd(imap, "RENAME", old, new, NULL);
   1111 	return imap_simpleresult(imap);
   1112 }
   1113 
   1114 llist_t *
   1115 imap_struct2num(llist_t *nlist)
   1116 {
   1117 	llistelem_t *elem;
   1118 	llist_t *ret, *lret;
   1119 
   1120 	ret = llist_new();
   1121 	forllist(nlist, elem) {
   1122 		if (elem->key != NULL && !strcmp(elem->key, "number")) {
   1123 			llist_add(ret, (char *)elem->data, NULL, 0);
   1124 			continue;
   1125 		}
   1126 
   1127 		if (elem->key == NULL && elem->data != NULL) {
   1128 			lret = imap_struct2num((llist_t *)elem->data);
   1129 			if (lret != NULL) {
   1130 				llist_listadd(ret, lret);
   1131 				llist_efree(lret);
   1132 			}
   1133 			continue;
   1134 		}
   1135 	}
   1136 
   1137 	if (ret->len < 1) {
   1138 		llist_efree(ret);
   1139 		return NULL;
   1140 	}
   1141 
   1142 	return ret;
   1143 }
   1144 
   1145 llist_t *
   1146 imap_searchresult(imap_t *imap)
   1147 {
   1148 	llist_t *res, *sret, *lret;
   1149 	llistelem_t *elem;
   1150 
   1151 	res = NULL;
   1152 	if (imap_result(imap, &res)) {
   1153 		if (res != NULL)
   1154 			llist_efree(res);
   1155 		return NULL;
   1156 	}
   1157 	if (res == NULL)
   1158 		return NULL;
   1159 
   1160 	if (!isstructlist(res)) {
   1161 		llist_efree(res);
   1162 		return NULL;
   1163 	}
   1164 
   1165 	sret = llist_new();
   1166 	forllist((llist_t *)res->last->data, elem) {
   1167 		if (elem == ((llist_t *)res->last->data)->first)
   1168 			continue;
   1169 
   1170 		if (elem->key == NULL) {
   1171 			if (elem->data != NULL) {
   1172 				lret = imap_struct2num((llist_t *)elem->data);
   1173 				if (lret != NULL) {
   1174 					llist_listadd(sret, lret);
   1175 					llist_free(lret);
   1176 				}
   1177 			}
   1178 			continue;
   1179 		}
   1180 		if (strcmp(elem->key, "number"))
   1181 			continue;
   1182 		if (elem->data == NULL)
   1183 			continue;
   1184 		llist_add(sret, (char *)elem->data, NULL, 0);
   1185 	}
   1186 	llist_efree(res);
   1187 	if (sret->len < 1) {
   1188 		llist_efree(sret);
   1189 		return NULL;
   1190 	}
   1191 
   1192 	return sret;
   1193 }
   1194 
   1195 llist_t *
   1196 imap_search(imap_t *imap, char *pattern)
   1197 {
   1198 	imap_cmd(imap, "SEARCH", "CHARSET", "UTF-8", pattern, NULL);
   1199 
   1200 	return imap_searchresult(imap);
   1201 }
   1202 
   1203 llist_t *
   1204 imap_sort(imap_t *imap, char *criteria, char *pattern)
   1205 {
   1206 	char *cstr;
   1207 
   1208 	cstr = smprintf("(%s)", criteria);
   1209 	imap_cmd(imap, "SORT", cstr, "UTF-8", pattern, NULL);
   1210 	free(cstr);
   1211 
   1212 	return imap_searchresult(imap);
   1213 }
   1214 
   1215 llist_t *
   1216 imap_thread(imap_t *imap, char *algorithm, char *pattern)
   1217 {
   1218 	imap_cmd(imap, "THREAD", algorithm, "UTF-8", pattern, NULL);
   1219 
   1220 	return imap_searchresult(imap);
   1221 }
   1222 
   1223 int
   1224 imap_select(imap_t *imap, char *mb)
   1225 {
   1226 	int rstat;
   1227 
   1228 	imap_cmd(imap, "SELECT", mb, NULL);
   1229 	rstat = imap_simpleresult(imap);
   1230 
   1231 	if (!rstat) {
   1232 		if (imap->selected != NULL)
   1233 			free(imap->selected);
   1234 		imap->selected = memdups(mb);
   1235 	}
   1236 
   1237 	return rstat;
   1238 }
   1239 
   1240 int
   1241 imap_store(imap_t *imap, llist_t *ids, char *item, llist_t *flags)
   1242 {
   1243 	char *flagcon, *flagstr, *idstr;
   1244 
   1245 	idstr = imap_ids2str(ids);
   1246 	flagcon = llist_joinstr(flags, " ");
   1247 	flagstr = smprintf("(%s)", flagcon);
   1248 	free(flagcon);
   1249 	imap_cmd(imap, "STORE", idstr, item, flagstr, NULL);
   1250 	free(idstr);
   1251 	free(flagstr);
   1252 
   1253 	return imap_simpleresult(imap);
   1254 }
   1255 
   1256 int
   1257 imap_setflags(imap_t *imap, llist_t *ids, llist_t *flags)
   1258 {
   1259 	return imap_store(imap, ids, "+FLAGS.SILENT", flags);
   1260 }
   1261 
   1262 int
   1263 imap_delflags(imap_t *imap, llist_t *ids, llist_t *flags)
   1264 {
   1265 	return imap_store(imap, ids, "-FLAGS.SILENT", flags);
   1266 }
   1267 
   1268 llist_t *
   1269 imap_getflags(imap_t *imap, llist_t *ids)
   1270 {
   1271 	llist_t *flags, *slist, *flist, *fflist;
   1272 	llistelem_t *elem, *line;
   1273 
   1274 	flags = imap_fetchprepare(imap, ids, "(FLAGS)");
   1275 	if (flags == NULL)
   1276 		return NULL;
   1277 
   1278 	forllist(flags, line) {
   1279 		slist = (llist_t *)line->data;
   1280 		flist = (llist_t *)slist->last->data;
   1281 		if (flist == NULL)
   1282 			continue;
   1283 
   1284 		fflist = llist_new();
   1285 		forllist(flist, elem) {
   1286 			if (!strcmp(elem->key, "atom"))
   1287 				llist_add(fflist, (char *)elem->data, NULL, 0);
   1288 		}
   1289 		llist_efree(flist);
   1290 		slist->last->data = fflist;
   1291 	}
   1292 
   1293 	return flags;
   1294 }
   1295 
   1296 int
   1297 imap_delete(imap_t *imap, llist_t *ids)
   1298 {
   1299 	llist_t *flags;
   1300 	int ret;
   1301 
   1302 	flags = llist_new();
   1303 	llist_add(flags, "\\Deleted", NULL, 0);
   1304 	ret = imap_setflags(imap, ids, flags);
   1305 	llist_free(flags);
   1306 
   1307 	return ret;
   1308 }
   1309 
   1310 int
   1311 imap_move(imap_t *imap, llist_t *ids, char *mb)
   1312 {
   1313 	if (imap_copy(imap, ids, mb))
   1314 		return 1;
   1315 	if (imap_delete(imap, ids))
   1316 		return 1;
   1317 	return 0;
   1318 }
   1319