rohrpost

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

mime.c (25659B)


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