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