mime.c (25707B)
1 /* 2 * Copy me if you can. 3 * by 20h 4 */ 5 6 #include <unistd.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <strings.h> 11 #include <ctype.h> 12 #include <iconv.h> 13 #include <errno.h> 14 #include <time.h> 15 16 #include "ind.h" 17 #include "llist.h" 18 #include "mime.h" 19 #include "parser.h" 20 #include "base64.h" 21 #include "quote.h" 22 #include "param.h" 23 #include "dos.h" 24 25 enum { 26 HEADER = 0x01, 27 HEADERVALUE, 28 }; 29 30 mime_t * 31 mime_new(void) 32 { 33 mime_t *part; 34 35 part = mallocz(sizeof(mime_t), 2); 36 part->hdrs = llist_new(); 37 part->parts = llist_new(); 38 part->state = HEADER; 39 40 return part; 41 } 42 43 void 44 mime_subfree(mime_t *mime, llistelem_t *elem) 45 { 46 forllist(mime->parts, elem) { 47 if (elem->key == NULL) 48 mime_free((mime_t *)elem->data); 49 elem->data = NULL; 50 } 51 llist_free(mime->parts); 52 } 53 54 void 55 mime_free(mime_t *mime) 56 { 57 mime_subfree(mime, NULL); 58 llist_free(mime->hdrs); 59 60 if (mime->body != NULL) 61 free(mime->body); 62 if (mime->partid != NULL) 63 free(mime->partid); 64 if (mime->ct != NULL) 65 free(mime->ct); 66 if (mime->cte != NULL) 67 free(mime->cte); 68 if (mime->charset != NULL) 69 free(mime->charset); 70 if (mime->boundary != NULL) 71 free(mime->boundary); 72 if (mime->rawhdrs != NULL) 73 free(mime->rawhdrs); 74 free(mime); 75 } 76 77 struct tm * 78 mime_parsedate(char *str) 79 { 80 struct tm tim; 81 82 memset(&tim, 0, sizeof(tim)); 83 if (strptime(str, "%a, %d %b %Y %T %z", &tim) != NULL) 84 return memdup(&tim, sizeof(tim)); 85 86 if (!strncmp(str, "Date: ", 6)) 87 str += 6; 88 89 /* 90 * Malformatted dates seen in the wild. 91 */ 92 if (strptime(str, "%a, %d %b %Y %T %Z", &tim) != NULL) 93 return memdup(&tim, sizeof(tim)); 94 95 if (strptime(str, "%d %b %Y %T %z", &tim) != NULL) 96 return memdup(&tim, sizeof(tim)); 97 98 if (strptime(str, "%a, %d %b %Y, %T %z", &tim) != NULL) 99 return memdup(&tim, sizeof(tim)); 100 101 if (strptime(str, "%d.%m.%Y", &tim) != NULL) 102 return memdup(&tim, sizeof(tim)); 103 104 return memdup(&tim, sizeof(tim)); 105 } 106 107 char * 108 mime_iconv(char *str, char *from, char *to) 109 { 110 iconv_t *ifd; 111 size_t left, avail, nconv; 112 char *outb, *strp, *outbp; 113 int olen, sd; 114 115 ifd = iconv_open(to, from); 116 if (ifd == (iconv_t)-1) 117 return NULL; 118 119 //printf("mime_iconv: '%s'; from='%s'\n", str, from); 120 121 left = strlen(str); 122 olen = left / 2; 123 avail = olen; 124 outb = mallocz(olen+1, 1); 125 outbp = outb; 126 strp = str; 127 for (;;) { 128 nconv = iconv(ifd, &strp, &left, &outbp, &avail); 129 if (nconv == (size_t)-1) { 130 if (errno == E2BIG) { 131 olen += 5; 132 sd = outbp - outb; 133 outb = reallocz(outb, olen+1, 0); 134 outbp = &outb[sd]; 135 avail += 5; 136 continue; 137 } 138 if (errno == EILSEQ || errno == EINVAL) 139 return NULL; 140 free(outb); 141 iconv_close(ifd); 142 return NULL; 143 } 144 break; 145 } 146 147 iconv_close(ifd); 148 if (outbp != NULL) 149 outbp[0] = '\0'; 150 return outb; 151 } 152 153 char * 154 mime_decodeheaderext(char *value) 155 { 156 char *work, *cret, *ret, *cs, *str, *enc, *ast, *dstr; 157 int len, slen; 158 159 len = strlen(value); 160 161 ret = memdupz(value, len); 162 work = memdupz(value, len); 163 164 if (!(work[0] == '=' && work[1] == '?' && work[len-1] == '=' 165 && work[len-2] == '?')) { 166 free(work); 167 return ret; 168 } 169 cs = &work[2]; 170 171 work[len-2] = '\0'; 172 enc = strchr(&work[2], '?'); 173 if (enc == NULL) { 174 free(work); 175 return ret; 176 } 177 enc[0] = '\0'; 178 enc++; 179 str = strchr(enc, '?'); 180 if (str == NULL) { 181 free(work); 182 return ret; 183 } 184 str[0] = '\0'; 185 str++; 186 187 /* 188 * RFC 2231 :( 189 * See: https://en.wikipedia.org/wiki/Mr._Mime 190 */ 191 ast = strchr(enc, '*'); 192 if (ast != NULL) 193 ast[0] = '\0'; 194 195 slen = strlen(str); 196 if (slen == 0) { 197 free(work); 198 free(ret); 199 return memdupz("", 1); 200 } 201 202 cret = NULL; 203 switch(enc[0]) { 204 case 'B': 205 case 'b': 206 cret = b64dec(str, &slen); 207 break; 208 case 'Q': 209 case 'q': 210 cret = qpdec(str, &slen, 1); 211 break; 212 } 213 214 //printf("mime_decodeheader: mime_iconv str='%s'; cret='%s';\n", str, cret); 215 if (cret != NULL) { 216 free(ret); 217 if (strcasecmp(cs, "utf-8")) { 218 dstr = mime_iconv(cret, cs, "UTF-8"); 219 if (dstr == NULL) { 220 str = smprintf("ERR(%s)", str); 221 } else { 222 str = dstr; 223 } 224 free(cret); 225 } else { 226 str = cret; 227 } 228 } else { 229 str = ret; 230 } 231 free(work); 232 233 return str; 234 } 235 236 int 237 mime_isextws(char *str, int len) 238 { 239 int i; 240 241 for (i = 0; i < len; i++) { 242 switch (str[i]) { 243 case '\n': 244 case '\r': 245 case ' ': 246 case '\t': 247 break; 248 default: 249 return 0; 250 } 251 } 252 return 1; 253 } 254 255 char * 256 mime_decodeheader(char *value) 257 { 258 char *work, *extp, *extw, *extb, *exte, *extr, *ret, *q1, *q2; 259 int vlen, rlen, elen, wasenc, i; 260 261 //printf("mime_decodeheader\n"); 262 ret = NULL; 263 rlen = 0; 264 vlen = strlen(value); 265 work = memdup(value, vlen+1); 266 extp = work; 267 wasenc = 0; 268 269 /* 270 * Avoid being tricked by malformed headers. 271 */ 272 for (i = 0; i < 32; i++) { 273 extb = strstr(extp, "=?"); 274 if (extb != NULL) { 275 elen = extb - extp; 276 if (extp != extb && (!wasenc || 277 !mime_isextws(extp, elen))) { 278 extw = memdupz(extp, elen); 279 ret = memdupcat(ret, rlen, extw, elen+1); 280 free(extw); 281 rlen += elen; 282 } 283 284 exte = NULL; 285 q1 = strchr(&extb[2], '?'); 286 if (q1 != NULL) { 287 q2 = strchr(&q1[1], '?'); 288 if (q2 != NULL) 289 exte = strstr(&q2[1], "?="); 290 } 291 if (exte != NULL) { 292 elen = &exte[2] - extb; 293 extw = memdupz(extb, elen); 294 extr = mime_decodeheaderext(extw); 295 free(extw); 296 elen = strlen(extr); 297 ret = memdupcat(ret, rlen, extr, elen+1); 298 rlen += elen; 299 free(extr); 300 extp = &exte[2]; 301 wasenc = 1; 302 continue; 303 } 304 } 305 break; 306 } 307 if ((extp - work) < vlen) 308 ret = memdupcat(ret, rlen, extp, strlen(extp)+1); 309 free(work); 310 311 /* Remove any space character, like newline. */ 312 if (ret != NULL) 313 strnormalizespace(ret); 314 315 return ret; 316 } 317 318 char *cstries[] = { 319 "utf-8", 320 "iso-8859-1", 321 "windows-1252", 322 "koi8", 323 "euc-jp" 324 "shift_jis", 325 "big5", 326 "iso-8859-15", 327 NULL 328 }; 329 330 char * 331 mime_guesscharset(char *str) 332 { 333 int i, eq; 334 char *itry; 335 336 for (i = 0; i < nelem(cstries); i++) { 337 itry = mime_iconv(str, cstries[i], cstries[i]); 338 if (itry == NULL) 339 continue; 340 eq = strcmp(str, itry); 341 free(itry); 342 if (!eq) 343 break; 344 } 345 346 return cstries[i]; 347 } 348 349 char * 350 mime_guessheader(char *value) 351 { 352 char *nvalue, *gcs; 353 354 gcs = NULL; 355 356 //printf("mime_guessheader '%s'\n", value); 357 358 nvalue = value; 359 if (!strisascii(value)) { 360 /* 361 * Guessing begins. Some major MUA developers did not read any 362 * RFCs. 363 */ 364 365 gcs = mime_guesscharset(value); 366 if (gcs != NULL) { 367 nvalue = mime_iconv(value, gcs, "utf-8"); 368 if (nvalue == NULL) { 369 nvalue = value; 370 gcs = NULL; 371 } 372 } 373 } 374 375 value = mime_decodeheader(nvalue); 376 if (gcs != NULL) 377 free(nvalue); 378 return value; 379 } 380 381 char * 382 mime_decodeparam(char *value) 383 { 384 char *work, *cret, *ret, *cs, *str, *lang, *dstr; 385 int len, slen; 386 387 len = strlen(value); 388 ret = memdup(value, len+1); 389 work = memdup(value, len+1); 390 391 cs = work; 392 lang = strchr(work, '\''); 393 if (lang == NULL) { 394 free(work); 395 return ret; 396 } 397 lang[0] = '\0'; 398 lang++; 399 str = strchr(lang, '\''); 400 if (str == NULL) { 401 free(work); 402 return ret; 403 } 404 str[0] = '\0'; 405 str++; 406 407 slen = strlen(str); 408 cret = paramdec(str, &slen); 409 410 if (cret != NULL) { 411 if (strcasecmp(cs, "utf-8")) { 412 free(ret); 413 dstr = mime_iconv(cret, cs, "UTF-8"); 414 if (dstr == NULL) { 415 str = smprintf("ERR(%s)", str); 416 } else { 417 str = dstr; 418 } 419 free(cret); 420 } else { 421 free(ret); 422 str = cret; 423 } 424 } else { 425 str = ret; 426 } 427 free(work); 428 429 return str; 430 } 431 432 char * 433 mime_encodestring(char *value) 434 { 435 char *b64, *ret; 436 437 if (strisascii(value)) 438 return memdups(value); 439 440 b64 = b64enc(value, strlen(value)); 441 ret = smprintf("=?UTF-8?b?%s?=", b64); 442 free(b64); 443 444 return ret; 445 } 446 447 char * 448 mime_encodeheader(char *header, char *value) 449 { 450 char *ret, *b64, *p, *mp, *str; 451 int hlen, lmax, isascii, firstline, slen; 452 453 isascii = 0; 454 455 /* 456 * RFC 2047: 457 * One encoded word should be at max. 75 characters. 458 * One encoded line is limited to 76 characters. 459 */ 460 hlen = strlen(header) + 2; 461 if (strisascii(value)) { 462 isascii = 1; 463 lmax = 75 - hlen; 464 } else { 465 lmax = 63 - hlen; 466 } 467 slen = strlen(value); 468 469 ret = NULL; 470 for (p = value, firstline = 1; slen > 0; slen -= lmax, p = mp) { 471 if (firstline) { 472 lmax += hlen; 473 firstline = 0; 474 } 475 476 mp = findlimitws(p, lmax); 477 if (mp == NULL) { 478 str = memdupz(p, slen); 479 } else { 480 str = memdupz(p, mp - p); 481 } 482 483 if (!isascii) { 484 b64 = b64enc(str, strlen(str)); 485 free(str); 486 mp = smprintf("=?UTF-8?b?%s?=", b64); 487 free(b64); 488 str = mp; 489 } 490 491 if (ret != NULL) { 492 mp = smprintf("%s %s", ret, str); 493 free(ret); 494 ret = mp; 495 } else { 496 ret = smprintf("%s", str); 497 } 498 } 499 500 return ret; 501 } 502 503 int 504 mime_paramsort(llistelem_t *elem1, llistelem_t *elem2) 505 { 506 int a, b; 507 char *n1, *n2; 508 509 n1 = strrchr(elem1->key, '*'); 510 if (n1 == NULL) 511 a = -1; 512 else 513 a = atoi(&n1[1]); 514 n2 = strrchr(elem2->key, '*'); 515 if (n2 == NULL) 516 b = -1; 517 else 518 b = atoi(&n2[1]); 519 520 return a - b; 521 } 522 523 /* 524 * Order and concatenate ordered params. 525 */ 526 llist_t * 527 mime_sanitizeparams(llist_t *params) 528 { 529 llistelem_t *param, *hit, *nparam; 530 llist_t *reorder, *hits; 531 char *key, *nvalue; 532 int klen, i, n; 533 534 reorder = llist_new(); 535 //printf("mime_sanitizeparams: start\n"); 536 n = 0; 537 forllist(params, param) { 538 if (n == 0) { 539 //printf("first key: %s\n", param->key); 540 n++; 541 continue; 542 } 543 544 key = param->key; 545 klen = strlen(key); 546 547 //printf("key = %s\n", key); 548 if (key[klen-2] == '*') { 549 nvalue = mime_decodeparam((char *)param->data); 550 if (nvalue != NULL) { 551 //printf("decoded: %s\n", nvalue); 552 free(param->data); 553 param->data = nvalue; 554 param->datalen = strlen(nvalue)+1; 555 } 556 key[klen-2] = '\0'; 557 //printf("key after = %s\n", key); 558 } 559 560 nvalue = strrchr(param->key, '*'); 561 if (nvalue == NULL) 562 continue; 563 for (i = 1; nvalue[i]; i++) 564 if (!isdigit(nvalue[i])) 565 break; 566 if (nvalue[i]) 567 continue; 568 569 nvalue[0] = '\0'; 570 if (llist_get(reorder, param->key) != NULL) 571 llist_add(reorder, param->key, NULL, 0); 572 nvalue[0] = '*'; 573 } 574 575 /* 576 * Sort and concatenate the return list. 577 */ 578 forllist(reorder, param) { 579 hits = llist_new(); 580 forllist(params, nparam) { 581 if (!strncmp(nparam->key, param->key, 582 strlen(param->key))) { 583 llist_add(hits, nparam->key, nparam->data, 584 nparam->datalen); 585 } 586 } 587 if (hits->len < 1) { 588 llist_free(hits); 589 continue; 590 } 591 592 nparam = llistelem_new(param->key, NULL, 0); 593 hits = llist_internsort(hits, mime_paramsort); 594 forllist(hits, hit) { 595 nparam->data = memdupcat(nparam->data, 596 nparam->datalen, hit->data, 597 hit->datalen); 598 nparam->datalen += hit->datalen-1; 599 } 600 601 params = llist_listdel(params, hits); 602 llist_free(hits); 603 llist_addelem(params, nparam); 604 } 605 llist_free(reorder); 606 607 return params; 608 } 609 610 llist_t * 611 mime_parseheader(char *field) 612 { 613 char *tok, *buf, *key, *value, *sep, *eq, quot; 614 llist_t *ret; 615 int tlen; 616 617 buf = memdups(field); 618 619 tok = buf; 620 ret = llist_new(); 621 //printf("mime_parseheader: buf = '%s'\n", buf); 622 while (tok[0] != '\0') { 623 key = NULL; 624 value = NULL; 625 626 /* 627 * 0.) Sanitize the beginning and the end. 628 */ 629 while (isspace(tok[0])) 630 tok++; 631 tlen = strlen(tok); 632 while (isspace(tok[tlen-1])) { 633 tok[tlen-1] = '\0'; 634 tlen--; 635 } 636 //printf("mime_parseheader: after sanitize: tok = '%s'\n", tok); 637 638 /* 639 * 1.) ([\t\r\v\f ]*)key 640 */ 641 key = tok + strspn(tok, "\t\r\v\f "); 642 //printf("mime_parseheader: key = '%s'\n", tok); 643 644 /* 645 * 2.) key 646 */ 647 tok = key + strcspn(key, "\t\r\v\f =;"); 648 if (tok[0] == ';' || tok[0] == '\0') { 649 quot = tok[0]; 650 tok[0] = '\0'; 651 if (strlen(key) > 0) { 652 //printf("mime_parseheader: add key '%s'\n", key); 653 llist_add(ret, key, NULL, 0); 654 } 655 if (quot != '\0') 656 tok++; 657 continue; 658 } 659 660 //printf("mime_parseheader: tok = '%s'\n", tok); 661 if (tok[0] == '=') { 662 eq = tok; 663 } else { 664 /* 665 * 3.) key([\t\r\v\f ]*)= 666 */ 667 tok[0] = '\0'; 668 eq = tok + 1 + strspn(tok+1, "\t\r\v\f ;"); 669 if (eq[0] == ';') { 670 if (strlen(key) > 0) 671 llist_add(ret, key, NULL, 0); 672 tok++; 673 continue; 674 } 675 676 if (eq[0] != '=') { 677 /* 678 * 3.1.) key; 679 */ 680 if (strlen(key) > 0) 681 llist_add(ret, key, NULL, 0); 682 tok++; 683 continue; 684 } 685 } 686 tok[0] = '\0'; 687 /* 688 * 4.) key=([\t\r\v\f ]*)("|)value 689 */ 690 tok = eq + 1 + strspn(eq+1, "\t\r\v\f "); 691 switch (tok[0]) { 692 case '"': 693 case '\'': 694 quot = tok[0]; 695 for (sep = tok+1; sep[0] != '\0'; sep++) { 696 if (sep[0] == quot) { 697 sep[0] = '\0'; 698 sep++; 699 break; 700 } 701 if (sep[0] == '\\' && sep[1] != '\0') 702 memmove(&sep[1], sep, strlen(&sep[1])); 703 } 704 value = &tok[1]; 705 tok = sep; 706 707 sep = tok + strcspn(tok, ";"); 708 if (sep[0] == ';') { 709 tok = sep + 1; 710 } else { 711 tok = sep; 712 } 713 break; 714 default: 715 /* 716 * 4.1.) value 717 */ 718 value = tok; 719 sep = tok + strcspn(tok, "\t\r\v\f ;"); 720 if (sep[0] == ';') { 721 sep[0] = '\0'; 722 tok = sep + 1; 723 } else { 724 tok = sep; 725 } 726 break; 727 } 728 729 //printf("mime_parseheader: add %s = '%s'\n", key, value); 730 llist_add(ret, key, value, strlen(value)+1); 731 } 732 free(buf); 733 734 //printf("ret->len = %d\n", ret->len); 735 if (ret->len > 0) 736 return mime_sanitizeparams(ret); 737 738 llist_free(ret); 739 return NULL; 740 } 741 742 void 743 mime_mkpartidsintern(mime_t *mime, char *sect, int pid) 744 { 745 llistelem_t *part; 746 747 mime->partid = smprintf("%s%d", sect, pid); 748 sect = smprintf("%s.", mime->partid); 749 750 pid = 1; 751 forllist(mime->parts, part) { 752 mime_mkpartidsintern((mime_t *)part->data, sect, pid); 753 pid++; 754 } 755 free(sect); 756 } 757 758 void 759 mime_mkpartids(mime_t *mime) 760 { 761 int pid; 762 llistelem_t *part; 763 764 mime->partid = memdupz("0", 1); 765 pid = 1; 766 forllist(mime->parts, part) 767 mime_mkpartidsintern((mime_t *)part->data, "", pid++); 768 } 769 770 /* 771 * This functions searches for the next boundary occurence. It will 772 * return *choice = 1, if it was an end boundary; otherwise 0. 773 */ 774 char * 775 mime_sgetbound(char *bound, char **p, char *max, int *len, int *choice) 776 { 777 char *ret, *op; 778 int slen, isenl, isend, sublen; 779 780 ret = NULL; 781 782 //printf("bound = '%s'\n", bound); 783 //printf("p = '%s'\n", *p); 784 slen = strlen(bound); 785 *choice = 0; 786 isenl = 0; 787 isend = 0; 788 sublen = 0; 789 790 for (;;) { 791 op = memmem(*p, (max-(*p)), bound, slen); 792 if (op == NULL) 793 return ret; 794 795 if (!strncmp(op+slen, "--", 2)) { 796 isend = 1; 797 if (op[slen+2] == '\n') 798 isenl = 1; 799 } else if (op[slen] == '\n') { 800 isenl = 1; 801 } 802 //printf("isenl = %d, isend = %d\n", isenl, isend); 803 804 if (op == *p) 805 break; 806 807 if (op > (*p + 1) && op[-2] == '\r' && op[-1] == '\n') 808 sublen = 2; 809 if (op > *p && op[-2] != '\r' && op[-1] == '\n') 810 sublen = 1; 811 //printf("sublen = %d\n", sublen); 812 break; 813 } 814 815 if (isend) { 816 *choice = 1; 817 slen += 2; 818 } 819 820 *len = op - *p - sublen; 821 ret = memdupz(*p, *len); 822 823 *p = op + slen + (isend * 2) + (2 - isenl); 824 825 //printf("p = '%s'\n", *p); 826 827 return ret; 828 } 829 830 mime_t * 831 mime_preparepart(mime_t *mime) 832 { 833 llistelem_t *hdr, *field; 834 llist_t *hdrf; 835 836 //printf("mime = %p\n", mime); 837 hdr = llist_ciget(mime->hdrs, "content-type"); 838 if (hdr != NULL && hdr->data != NULL && strlen(hdr->data) > 0) { 839 //printf("content-type: %s\n", (char *)hdr->data); 840 hdrf = mime_parseheader(hdr->data); 841 //printf("hdrf = %p\n", hdrf); 842 //printf("%s\n", hdrf->first->key); 843 if (hdrf != NULL) { 844 if (!strncasecmp(hdrf->first->key, "multipart", 9)) { 845 //printf("is multipart\n"); 846 field = llist_ciget(hdrf, "boundary"); 847 if (field == NULL) { 848 return NULL; 849 //die("Could not find boundary " 850 // "in multipart!\n"); 851 } 852 mime->boundary = smprintf("--%s", 853 (char *)field->data); 854 //printf("boundary: \"%s\"\n", mime->boundary); 855 } 856 mime->ct = memdups(hdrf->first->key); 857 858 field = llist_ciget(hdrf, "charset"); 859 if (field != NULL && field->data != NULL) { 860 mime->charset = memdupz(field->data, 861 field->datalen); 862 } 863 864 llist_free(hdrf); 865 } 866 } 867 868 if (mime->ct == NULL) 869 mime->ct = memdupz("text/plain", 10); 870 //printf("mime->ct = %s\n", mime->ct); 871 if (mime->charset == NULL) 872 mime->charset = memdupz("iso8859-1", 9); 873 //printf("mime->charset = %s\n", mime->charset); 874 875 hdr = llist_ciget(mime->hdrs, "Content-Transfer-Encoding"); 876 if (hdr != NULL && hdr->data != NULL) { 877 mime->cte = memdupz(hdr->data, hdr->datalen); 878 } else { 879 mime->cte = memdupz("7bit", 4); 880 } 881 //printf("mime->cte = %s\n", mime->cte); 882 883 return mime; 884 } 885 886 mime_t * 887 mime_parsebufintern(mime_t *mime, char *str, int len) 888 { 889 int i, partlen, isend, blen; 890 char *p, *rp, buf[1025], *key, *value, *tvalue, *part; 891 llistelem_t *hdr; 892 mime_t *partm; 893 894 rp = str; 895 p = str; 896 for (; (rp = sgets(buf, sizeof(buf)-1, &p));) { 897 //printf("line '%s'\n", buf); 898 blen = strlen(buf); 899 if (buf[blen-1] == '\r') 900 buf[blen-1] = '\0'; 901 902 switch (mime->state) { 903 case HEADERVALUE: 904 switch (buf[0]) { 905 case ' ': 906 case '\t': 907 case '\r': 908 case '\f': 909 case '\v': 910 //printf("hdrvalue: %s (%d)\n", buf, 911 // (int)strlen(buf)); 912 /* 913 * " value" 914 */ 915 sscanf(buf, "%*[ \t\r\v\f]%1024m[^\n]", 916 &value); 917 if (value != NULL && hdr != NULL) { 918 if (hdr->data != NULL) { 919 part = memdup(value, strlen(value)+1); 920 921 /* Adding a space. */ 922 hdr->data = memdupcat(hdr->data, 923 hdr->datalen-1, 924 " ", 1); 925 hdr->datalen++; 926 927 /* Adding the next line. */ 928 i = strlen(part); 929 key = memdupcat(hdr->data, 930 hdr->datalen-1, 931 part, i+1); 932 free(part); 933 hdr->data = key; 934 hdr->datalen += i; 935 //printf("%s = %s\n", hdr->key, 936 // (char *)hdr->data); 937 } 938 free(value); 939 } 940 goto mimeparsebufagain; 941 default: 942 break; 943 } 944 945 if (hdr != NULL) 946 hdr = NULL; 947 mime->state = HEADER; 948 /* FALL THROUGH: No header value found. */ 949 case HEADER: 950 //printf("hdr: %s\n", buf); 951 952 /* 953 * End of headers. 954 */ 955 if (strlen(buf) == 0) { 956 mime->rawhdrs = memdupz(str, (p - str)); 957 mime->rawhdrslen = p - str; 958 goto mimeparsebufbodyparse; 959 } 960 961 /* 962 * "key: value" 963 */ 964 key = NULL; 965 value = NULL; 966 tvalue = NULL; 967 sscanf(buf, "%1024m[^: \t\r\v\f]:" 968 "%1024m[^\n]", &key, &value); 969 if (value == NULL) 970 value = memdupz(" ", 2); 971 //printf("%s = %s\n", key, value); 972 if (key != NULL && value != NULL) { 973 tvalue = value + strspn(value, 974 " \t\r\v\f"); 975 hdr = llistelem_new(key, tvalue, 976 strlen(tvalue)+1); 977 llist_addelem(mime->hdrs, hdr); 978 mime->state = HEADERVALUE; 979 } 980 if (key != NULL) 981 free(key); 982 if (value != NULL) 983 free(value); 984 break; 985 default: 986 mimeparsebufagain: 987 break; 988 } 989 } 990 //printf("return mime_preparepart\n"); 991 return mime_preparepart(mime); 992 993 mimeparsebufbodyparse: 994 //printf("body parsing begins.\n"); 995 mime = mime_preparepart(mime); 996 if (mime == NULL) 997 return NULL; 998 999 /* 1000 * It is not a multipart message, so take the remainder 1001 * of the given message. 1002 */ 1003 if (mime->boundary == NULL) { 1004 //printf("No boundary there. Taking the remainder.\n"); 1005 partlen = str - p + len; 1006 mime->body = memdupz(p, partlen); 1007 mime->bodylen = partlen; 1008 //printf("strlen = %ld; partlen = %d;\n", strlen(mime->body), 1009 // partlen); 1010 //printf("mime->body = \"%s\"\n", mime->body); 1011 1012 return mime; 1013 } else { 1014 //printf("There is a boundary.\n"); 1015 } 1016 1017 partlen = 0; 1018 //printf("p = \"%s\"\n", p); 1019 mime->body = mime_sgetbound(mime->boundary, &p, str + len - 1, 1020 &partlen, &isend); 1021 mime->bodylen = partlen; 1022 if (isend) { 1023 /* 1024 * This is an end boundary at the beginning 1025 * of a multipart message. Abort. 1026 */ 1027 //die("End boundary at beginning of multipart.\n"); 1028 return mime; 1029 } 1030 if (mime->body == NULL) { 1031 //die("Could not find beginning MIME content.\n"); 1032 return mime; 1033 } 1034 //printf("mime->body = \"%s\"\n", mime->body); 1035 1036 for(;;) { 1037 partlen = 0; 1038 part = mime_sgetbound(mime->boundary, &p, str + len - 1, 1039 &partlen, &isend); 1040 //printf("part = \"%s\"\n", part); 1041 if (part == NULL) { 1042 /* 1043 * There maybe no ending boundary. Some e-mail 1044 * signing applications forget this. 1045 */ 1046 if (p < (str + len - 1)) { 1047 partlen = str - p + len; 1048 part = memdupz(p, partlen); 1049 p = str + len - 1; 1050 } else { 1051 break; 1052 } 1053 } 1054 1055 partm = mime_new(); 1056 partm = mime_parsebufintern(partm, part, partlen); 1057 if (partm != NULL) 1058 llist_addraw(mime->parts, NULL, partm, sizeof(partm)); 1059 free(part); 1060 1061 if (isend) 1062 break; 1063 } 1064 1065 return mime; 1066 } 1067 1068 mime_t * 1069 mime_parsebuf(char *str, int len) 1070 { 1071 mime_t *ret, *pret; 1072 1073 ret = mime_new(); 1074 pret = mime_parsebufintern(ret, str, len); 1075 if (pret == NULL) { 1076 mime_free(ret); 1077 return NULL; 1078 } 1079 1080 mime_mkpartids(ret); 1081 1082 return ret; 1083 } 1084 1085 char * 1086 mime_searchsplit(char *data, int klen) 1087 { 1088 char *p, *op; 1089 int incomment; 1090 1091 if (strlen(data) + klen <= 74) 1092 return NULL; 1093 1094 p = &data[73 - klen]; 1095 op = p; 1096 incomment = 0; 1097 1098 for (;;) { 1099 switch (p[0]) { 1100 case '"': 1101 case '\'': 1102 /* 1103 * This is meant to be broken. 1104 * It's just heuristics. 1105 */ 1106 incomment = !incomment; 1107 break; 1108 case ' ': 1109 case '\t': 1110 case '\f': 1111 case '\n': 1112 case '\r': 1113 if (incomment) 1114 break; 1115 return p; 1116 case '\0': 1117 return &data[73 - klen]; 1118 } 1119 1120 if (p == data) { 1121 p = op; 1122 op = NULL; 1123 continue; 1124 } 1125 1126 if (op != NULL) { 1127 p--; 1128 } else { 1129 p++; 1130 } 1131 } 1132 1133 return NULL; 1134 } 1135 1136 char * 1137 mime_printheader(llistelem_t *hdr) 1138 { 1139 char *buf, *sp, *osp; 1140 int blen, splen; 1141 1142 blen = 0; 1143 sp = mime_searchsplit((char *)hdr->data, strlen(hdr->key) + 2); 1144 if (sp != NULL) { 1145 buf = smprintf("%s: ", hdr->key); 1146 blen = strlen(buf); 1147 1148 buf = memdupcat(buf, blen, (char *)hdr->data, 1149 (sp - (char *)hdr->data)); 1150 blen += (sp - (char *)hdr->data); 1151 buf = memdupcat(buf, blen, "\r\n", 2); 1152 blen += 2; 1153 1154 for (osp = sp;; osp = sp) { 1155 sp = mime_searchsplit(osp, 8); 1156 if (sp == NULL) 1157 break; 1158 1159 buf = memdupcat(buf, blen, "\t", 1); 1160 blen += 1; 1161 buf = memdupcat(buf, blen, osp, (sp - osp)); 1162 blen += (sp - osp); 1163 buf = memdupcat(buf, blen, "\r\n", 2); 1164 blen += 2; 1165 } 1166 1167 if (strlen(osp) > 0) { 1168 buf = memdupcat(buf, blen, "\t", 1); 1169 blen += 1; 1170 splen = strlen(osp); 1171 buf = memdupcat(buf, blen, osp, splen); 1172 blen += splen; 1173 buf = memdupcat(buf, blen, "\r\n", 2); 1174 } 1175 } else { 1176 buf = smprintf("%s: %s\r\n", hdr->key, (char *)hdr->data); 1177 } 1178 1179 return buf; 1180 } 1181 1182 char * 1183 mime_printbuf(mime_t *mime, int *len) 1184 { 1185 llistelem_t *hdr; 1186 char *ret, *abuf; 1187 int rlen, alen; 1188 1189 rlen = 0; 1190 ret = NULL; 1191 1192 forllist(mime->hdrs, hdr) { 1193 abuf = mime_printheader(hdr); 1194 alen = strlen(abuf); 1195 1196 ret = memdupcat(ret, rlen, abuf, alen); 1197 rlen += alen; 1198 free(abuf); 1199 /* 1200 * TODO: Add part handling. 1201 */ 1202 } 1203 1204 ret = memdupcat(ret, rlen, "\r\n", 2); 1205 rlen += 2; 1206 1207 return ret; 1208 } 1209 1210 void 1211 printtabs(int depth) 1212 { 1213 for (; depth; depth--) 1214 printf("\t"); 1215 } 1216 1217 void 1218 mime_printintern(mime_t *mime, int depth) 1219 { 1220 llistelem_t *elem; 1221 1222 printtabs(depth); 1223 printf("partid: %s\n", mime->partid); 1224 printtabs(depth); 1225 printf("hdr:\n"); 1226 forllist(mime->hdrs, elem) { 1227 printtabs(depth); 1228 printf("%s = %s\n", elem->key, (char *)elem->data); 1229 } 1230 1231 printtabs(depth); 1232 printf("body:\n"); 1233 printtabs(depth); 1234 printf("%d\n", mime->bodylen); 1235 printf("%s", mime->body); 1236 1237 if (mime->parts->len > 0) { 1238 printtabs(depth); 1239 printf("parts:\n"); 1240 forllist(mime->parts, elem) 1241 mime_printintern((mime_t *)elem->data, depth+1); 1242 } 1243 } 1244 1245 void 1246 mime_print(mime_t *mime) 1247 { 1248 mime_printintern(mime, 0); 1249 } 1250 1251 char * 1252 mime_decodepartencoding(mime_t *mime, int *len) 1253 { 1254 char *ret; 1255 1256 //printf("ct = \"%s\"\n", mime->ct); 1257 //printf("cte = \"%s\"\n", mime->cte); 1258 ret = NULL; 1259 if (!strcasecmp(mime->cte, "base64")) { 1260 *len = mime->bodylen; 1261 ret = b64dec(mime->body, len); 1262 } else if (!strcasecmp(mime->cte, "quoted-printable")) { 1263 *len = mime->bodylen; 1264 ret = qpdec(mime->body, len, 0); 1265 } else if (!strncasecmp(mime->ct, "text/", 5)) { 1266 /* Convert CRLF to LF. */ 1267 *len = mime->bodylen; 1268 ret = dosdec(mime->body, len); 1269 } 1270 1271 if (ret == NULL && mime->body != NULL && mime->bodylen > 0) { 1272 *len = mime->bodylen; 1273 ret = memdupz(mime->body, mime->bodylen); 1274 } 1275 1276 return ret; 1277 } 1278 1279 char * 1280 mime_decodepart(mime_t *mime, int *len) 1281 { 1282 char *ret, *cret; 1283 1284 if (mime->bodylen == 0) { 1285 *len = 0; 1286 return memdupz("", 1); 1287 } 1288 1289 ret = mime_decodepartencoding(mime, len); 1290 if (ret == NULL) { 1291 *len = 0; 1292 return memdupz("", 1); 1293 } 1294 1295 if (strcasecmp(mime->cte, "binary")) { 1296 if (strcasecmp(mime->charset, "utf-8")) { 1297 cret = mime_iconv(ret, mime->charset, "UTF-8"); 1298 if (cret != NULL) { 1299 free(ret); 1300 ret = cret; 1301 } 1302 *len = strlen(ret); 1303 } 1304 } 1305 1306 return ret; 1307 } 1308 1309 char * 1310 mime_filename(mime_t *mime) 1311 { 1312 char *filename; 1313 llistelem_t *hdr, *name; 1314 llist_t *hdrp; 1315 1316 filename = NULL; 1317 1318 /* 1319 * 1.) The standard. 1320 */ 1321 hdr = llist_ciget(mime->hdrs, "Content-Disposition"); 1322 if (hdr != NULL && hdr->data != NULL) { 1323 hdrp = mime_parseheader((char *)hdr->data); 1324 if (hdrp != NULL) { 1325 name = llist_ciget(hdrp, "filename"); 1326 if (name != NULL && name->data != NULL) { 1327 filename = mime_guessheader( 1328 (char *)name->data); 1329 } 1330 llist_free(hdrp); 1331 } 1332 1333 if (filename != NULL) 1334 return filename; 1335 } 1336 1337 /* 1338 * 2.) The modern age. 1339 */ 1340 hdr = llist_ciget(mime->hdrs, "Content-Type"); 1341 if (hdr != NULL && hdr->data != NULL) { 1342 hdrp = mime_parseheader((char *)hdr->data); 1343 if (hdrp != NULL) { 1344 name = llist_ciget(hdrp, "name"); 1345 if (name != NULL && name->data != NULL) { 1346 filename = mime_guessheader( 1347 (char *)name->data); 1348 } 1349 llist_free(hdrp); 1350 } 1351 1352 if (filename != NULL) 1353 return filename; 1354 } 1355 1356 return NULL; 1357 } 1358 1359 1360 char * 1361 mime_mkfilename(char *id, mime_t *mime) 1362 { 1363 char *filename; 1364 llistelem_t *hdr; 1365 1366 filename = mime_filename(mime); 1367 if (filename != NULL) 1368 return filename; 1369 1370 /* 1371 * 3.) The ugly. 1372 */ 1373 hdr = llist_ciget(mime->hdrs, "Content-Description"); 1374 if (hdr != NULL && hdr->data != NULL) { 1375 filename = mime_guessheader((char *)hdr->data); 1376 if (filename != NULL) 1377 return filename; 1378 } 1379 1380 /* 1381 * 4.) Last resort. 1382 */ 1383 if (id == NULL) 1384 id = "000"; 1385 return smprintf("%s.%s.part", id, mime->partid); 1386 } 1387 1388 char * 1389 mime_mkboundary(void) 1390 { 1391 srand(time(NULL)); 1392 return smprintf("=--= _TUlNRSBTdWNrcyEK/%x_ =--=", rand()); 1393 } 1394