rohrpost

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

imap.c (23108B)


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