imap.c - rohrpost - A commandline mail client to change the world as we see it.
(HTM) git clone git://r-36.net/rohrpost
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) 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