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