Annotation of libaitwww/src/mime.c, revision 1.1.1.1.2.1
1.1 misho 1: /*************************************************************************
2: * (C) 2012 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
3: * by Michael Pounov <misho@elwix.org>
4: *
5: * $Author: misho $
1.1.1.1.2.1! misho 6: * $Id: mime.c,v 1.1.1.1 2012/03/08 23:40:21 misho Exp $
1.1 misho 7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47: #include "mime.h"
48: #include "tools.h"
49:
50:
51: static int decode_quoted(char *, int, char *);
52: static int decode_base64(char *, int, char *);
53:
54: static const char *n_encode[] = { "7bit", "8bit", "binary" };
55: static struct _tagEncode {
56: char *name;
57: float mul;
58:
59: int (*decode)(char *, int, char *);
60: } encode[] = {
61: { "quoted-printable", 1, decode_quoted },
62: { "base64", (float) 3 / 4, decode_base64 }
63: };
64:
65:
66: static inline char *
67: bd_begin(const char *str)
68: {
69: char *s;
70: int len = strlen(str) + 6;
71:
72: s = malloc(len + 1);
73: if (!s) {
74: LOGERR;
75: return NULL;
76: } else {
77: snprintf(s, len + 1, "\r\n--%s\r\n", str);
78: s[len] = 0;
79: }
80:
81: return s;
82: }
83:
84: static inline char *
85: bd_end(const char *str)
86: {
87: char *s;
88: int len = strlen(str) + 8;
89:
90: s = malloc(len + 1);
91: if (!s) {
92: LOGERR;
93: return NULL;
94: } else {
95: snprintf(s, len + 1, "\r\n--%s--\r\n", str);
96: s[len] = 0;
97: }
98:
99: return s;
100: }
101:
102: static u_int
103: powmod(int x, int y, int q)
104: {
105: u_int ret = 1;
106:
107: while (y) {
108: if (y & 1)
109: ret = ((unsigned long long)ret * x) % q;
110: x = (unsigned long long) x * x % q;
111: y = y / 2;
112: }
113: return ret;
114: }
115:
116: static const char *
117: findtextpos(const char *T, size_t tlen, const char *P, size_t plen)
118: {
119: const u_int q = 4294967291u;
120: const u_int d = 256;
121: u_int hash, p = 0, t = 0;
122: register int i;
123:
124: hash = powmod(d, plen - 1, q);
125:
126: /* calculate initial hash tags */
127: for (i = 0; i < plen; i++) {
128: p = (d * p + P[i]) % q;
129: t = (d * t + T[i]) % q;
130: }
131:
132: tlen -= plen;
133: for (i = 0; i <= tlen; i++) {
134: if (p == t) {
135: /* match pattern */
136: if (!memcmp(P, T + i, plen))
137: return T + i;
138: }
139:
140: /* rehashing */
141: if (i < tlen)
142: t = (d * (t - T[i] * hash) + T[i + plen]) % q;
143: }
144:
145: return NULL;
146: }
147:
148: static inline void
149: freeHeader(struct tagMIME * __restrict m)
150: {
151: struct tagCGI *c;
152:
153: while ((c = SLIST_FIRST(&m->mime_header))) {
154: if (c->cgi_name)
155: free(c->cgi_name);
156: if (c->cgi_value)
157: free(c->cgi_value);
158: SLIST_REMOVE_HEAD(&m->mime_header, cgi_node);
159: free(c);
160: }
161: }
162:
163: static char *
164: hdrValue(const char *str, size_t len, const char **end)
165: {
166: const char *e, *crlf = NULL;
167: char *tmp, *s = NULL;
168: int off = 0;
169:
170: e = str + len;
171: while (str < e) {
172: if ((crlf = findtextpos(str, e - str, CRLF, strlen(CRLF)))) {
173: www_SetErr(EBADMSG, "Bad MIME format message");
174: return NULL;
175: }
176:
177: tmp = realloc(s, crlf - str + off + 1);
178: if (!tmp) {
179: LOGERR;
180: free(s);
181: return NULL;
182: } else
183: s = tmp;
184:
185: memcpy(s + off, str, crlf - str);
186: s[crlf - str + off] = 0;
187: off += crlf - str;
188:
189: /* if is multi part header value */
190: tmp = (char*) crlf + strlen(CRLF);
191: if (*tmp == ' ' || *tmp == '\t')
192: str = ++tmp;
193: else
194: break;
195: }
196:
197: *end = crlf + strlen(CRLF);
198: return s;
199: }
200:
201: static inline int
202: hexdigit(char a)
203: {
204: if (a >= '0' && a <= '9')
205: return a - '0';
206: if (a >= 'a' && a <= 'f')
207: return a - 'a' + 10;
208: if (a >= 'A' && a <= 'F')
209: return a - 'A' + 10;
210: /* error!!! */
211: return -1;
212: }
213:
214: static int
215: decode_quoted(char *in, int len, char *out)
216: {
217: register int i, cx;
218:
219: for (i = cx = 0; i < len; i++)
220: if (in[i] == '=') {
221: /* special handling */
222: i++;
223: if ((in[i] >= '0' && in[i] <= '9') ||
224: (in[i] >= 'A' && in[i] <= 'F') ||
225: (in[i] >= 'a' && in[i] <= 'f')) {
226: /* encoding a special char */
227: *out++ = hexdigit(in[i]) << 4 | hexdigit(in[i+ 1]);
228: cx++;
229: } else
230: i += strlen(CRLF);
231: } else {
232: *out++ = in[i++];
233: cx++;
234: }
235:
236: return cx;
237: }
238:
239: static int
240: decode_base64(char *in, int len, char *out)
241: {
242: register int cx, i, j;
243: int bits, eqc;
244:
245: for (cx = i = eqc = bits = 0; i < len && !eqc; bits = 0) {
246: for (j = 0; i < len && j < 4; i++) {
247: switch (in[i]) {
248: case 'A': case 'B': case 'C': case 'D': case 'E':
249: case 'F': case 'G': case 'H': case 'I': case 'J':
250: case 'K': case 'L': case 'M': case 'N': case 'O':
251: case 'P': case 'Q': case 'R': case 'S': case 'T':
252: case 'U': case 'V': case 'W': case 'X': case 'Y':
253: case 'Z':
254: bits = (bits << 6) | (in[i] - 'A');
255: j++;
256: break;
257: case 'a': case 'b': case 'c': case 'd': case 'e':
258: case 'f': case 'g': case 'h': case 'i': case 'j':
259: case 'k': case 'l': case 'm': case 'n': case 'o':
260: case 'p': case 'q': case 'r': case 's': case 't':
261: case 'u': case 'v': case 'w': case 'x': case 'y':
262: case 'z':
263: bits = (bits << 6) | (in[i] - 'a' + 26);
264: j++;
265: break;
266: case '0': case '1': case '2': case '3': case '4':
267: case '5': case '6': case '7': case '8': case '9':
268: bits = (bits << 6) | (in[i] - '0' + 52);
269: j++;
270: break;
271: case '+':
272: bits = (bits << 6) | 62;
273: j++;
274: break;
275: case '/':
276: bits = (bits << 6) | 63;
277: j++;
278: break;
279: case '=':
280: bits <<= 6;
281: j++;
282: eqc++;
283: break;
284: default:
285: break;
286: }
287: }
288:
289: if (!j && i >= len)
290: continue;
291:
292: switch (eqc) {
293: case 0:
294: *out++ = (bits >> 16) & 0xff;
295: *out++ = (bits >> 8) & 0xff;
296: *out++ = bits & 0xff;
297: cx += 3;
298: break;
299: case 1:
300: *out++ = (bits >> 16) & 0xff;
301: *out++ = (bits >> 8) & 0xff;
302: cx += 2;
303: break;
304: case 2:
305: *out++ = (bits >> 16) & 0xff;
306: cx += 1;
307: break;
308: }
309: }
310:
311: return cx;
312: }
313:
314: /* ------------------------------------------------------------------ */
315:
316: /*
317: * mime_parseMultiPart() - Parse multi part MIME message
318: *
319: * @str = String
320: * @len = String length
321: * @bd = Boundary tag
322: * @end = End of parsed part
323: * return: NULL error or !=NULL allocated MIME session
324: */
325: mime_t *
326: mime_parseMultiPart(const char *str, size_t len, const char *bdtag, const char **end)
327: {
328: mime_t *mime = NULL;
329: struct iovec bd[2];
330: struct tagMIME *m, *old = NULL;
331: const char *next;
332:
333: if (!str | !bdtag) {
334: www_SetErr(EINVAL, "String or boundary tag is NULL");
335: return NULL;
336: }
337:
338: /* init MIME */
339: mime = malloc(sizeof(mime_t));
340: if (!mime) {
341: LOGERR;
342: return NULL;
343: } else {
344: memset(mime, 0, sizeof(mime_t));
345: SLIST_INIT(mime);
346: }
347:
348: /* prepare boundary format */
349: bd[0].iov_base = bd_begin(bdtag);
350: if (!bd[0].iov_base) {
351: free(mime);
352: return NULL;
353: } else
354: bd[0].iov_len = strlen(bd[0].iov_base);
355: bd[1].iov_base = bd_end(bdtag);
356: if (!bd[1].iov_base) {
357: free(bd[0].iov_base);
358: free(mime);
359: return NULL;
360: } else
361: bd[1].iov_len = strlen(bd[1].iov_base);
362: if (memcmp(str, strstr(bd[0].iov_base, "--"), strlen(strstr(bd[0].iov_base, "--")))) {
363: www_SetErr(EBADMSG, "Bad content data, not found boundary tag");
364: free(bd[1].iov_base);
365: free(bd[0].iov_base);
366: free(mime);
367: return NULL;
368: } else {
369: str += strlen(strstr(bd[0].iov_base, "--"));
370: len -= strlen(strstr(bd[0].iov_base, "--"));
371: }
372:
373: while (42) {
374: m = malloc(sizeof(struct tagMIME));
375: if (!m) {
376: LOGERR;
377: mime_close(&mime);
378: free(bd[1].iov_base);
379: free(bd[0].iov_base);
380: return NULL;
381: } else {
382: memset(m, 0, sizeof(struct tagMIME));
383: SLIST_INIT(&m->mime_header);
384: }
385:
386: if (!(next = findtextpos(str, len, bd[0].iov_base, bd[0].iov_len)))
387: next = findtextpos(str, len, bd[1].iov_base, bd[1].iov_len);
388:
389: /* parse message between tags */
390: if (mime_readPart(m, str, next - str)) {
391: mime_close(&mime);
392: free(bd[1].iov_base);
393: free(bd[0].iov_base);
394: return NULL;
395: }
396:
397: str += next - str;
398: len -= next - str;
399:
400: /* add to mime session */
401: if (!old)
402: SLIST_INSERT_HEAD(mime, m, mime_node);
403: else
404: SLIST_INSERT_AFTER(old, m, mime_node);
405: old = m;
406:
407: /* match part termination tag */
408: if (!memcmp(str, bd[1].iov_base, bd[1].iov_len))
409: break;
410:
411: str += bd[0].iov_len;
412: len -= bd[0].iov_len;
413: }
414:
415: str += bd[0].iov_len;
1.1.1.1.2.1! misho 416: /* LLVM static code analyzer said for this - unusable
! 417: *
1.1 misho 418: len -= bd[0].iov_len;
1.1.1.1.2.1! misho 419: */
1.1 misho 420:
421: if (end)
422: *end = str;
423: return mime;
424: }
425:
426: static inline void
427: freeMIME(struct tagMIME * __restrict m)
428: {
429: if (m->mime_body.iov_base)
430: free(m->mime_body.iov_base);
431: if (m->mime_prolog.iov_base)
432: free(m->mime_prolog.iov_base);
433: if (m->mime_epilog.iov_base)
434: free(m->mime_epilog.iov_base);
435:
436: freeHeader(m);
437: mime_close(&m->mime_attach);
438: }
439:
440: /*
441: * mime_close() - Close MIME session and free all resources
442: *
443: * @mime = Inited mime session
444: * return: none
445: */
446: void
447: mime_close(mime_t ** __restrict mime)
448: {
449: struct tagMIME *m;
450:
451: if (!mime || !*mime)
452: return;
453:
454: while ((m = SLIST_FIRST(*mime))) {
455: SLIST_REMOVE_HEAD(*mime, mime_node);
456: freeMIME(m);
457: free(m);
458: }
459:
460: free(*mime);
461: *mime = NULL;
462: }
463:
464: /*
465: * mime_parseHeader() - Parse MIME header pairs
466: *
467: * @m = Mime part
468: * @str = String
469: * @len = String length
470: * @end = End of parsed part
471: * return: -1 error or 0 ok
472: */
473: int
474: mime_parseHeader(struct tagMIME * __restrict m, const char *str, size_t len, const char **end)
475: {
476: const char *e, *colon, *eoh;
477: struct tagCGI *c, *old = NULL;
478:
479: if (!m || !str) {
480: www_SetErr(EINVAL, "Mime part or string is NULL");
481: return -1;
482: } else
483: e = str + len;
484:
485: while (str < e) {
486: if (!memcmp(str, CRLF, strlen(CRLF))) {
487: str += 2;
488: break;
489: }
490:
491: colon = memchr(str, ':', e - str);
492: eoh = findtextpos(str, e - str, CRLF, strlen(CRLF));
493: if (!colon || !eoh || colon > eoh) {
494: www_SetErr(EBADMSG, "Bad MIME format message");
495: freeHeader(m);
496: return -1;
497: }
498:
499: c = malloc(sizeof(struct tagCGI));
500: if (!c) {
501: LOGERR;
502: freeHeader(m);
503: return -1;
504: }
505: /* get name */
506: c->cgi_name = malloc(colon - str + 1);
507: if (!c->cgi_name) {
508: LOGERR;
509: free(c);
510: freeHeader(m);
511: return -1;
512: } else {
513: memcpy(c->cgi_name, str, colon - str);
514: c->cgi_name[colon - str] = 0;
515: }
516: /* get value */
517: c->cgi_value = hdrValue(colon + 1, e - colon - 1, &str);
518:
519: if (!old)
520: SLIST_INSERT_HEAD(&m->mime_header, c, cgi_node);
521: else
522: SLIST_INSERT_AFTER(old, c, cgi_node);
523: old = c;
524: }
525:
526: if (end)
527: *end = str;
528: return 0;
529: }
530:
531: /*
532: * mime_getValue() - Get value from MIME header
533: *
534: * @m = Mime part
535: * @name = Header name
536: * return: NULL not found or !=NULL value
537: */
538: inline const char *
539: mime_getValue(struct tagMIME * __restrict m, const char *name)
540: {
541: struct tagCGI *c;
542: const char *v = NULL;
543:
544: SLIST_FOREACH(c, &m->mime_header, cgi_node)
545: if (!strcmp(c->cgi_name, name)) {
546: v = c->cgi_value;
547: break;
548: }
549: return v;
550: }
551:
552: /*
553: * mime_readPart() Read and parse MIME part
554: *
555: * @m = Mime part
556: * @str = String
557: * @len = String length
558: * return: -1 error or 0 ok
559: */
560: int
561: mime_readPart(struct tagMIME * __restrict m, const char *str, size_t len)
562: {
563: const char *eoh, *ct, *eb;
564: cgi_t *attr;
565: struct iovec bd;
566:
567: if (!m || !str) {
568: www_SetErr(EINVAL, "Mime part or string is NULL");
569: return -1;
570: }
571:
572: if (mime_parseHeader(m, str, len, &eoh))
573: return -1;
574:
575: ct = mime_getValue(m, "content-type");
576: if (!ct || www_cmptype(ct, "multipart")) {
577: /* not multi part, assign like body element */
578: m->mime_body.iov_base = malloc(len - (eoh - str) + 1);
579: memcpy(m->mime_body.iov_base, eoh, len - (eoh - str));
580: ((char*) m->mime_body.iov_base)[len - (eoh - str)] = 0;
581: m->mime_body.iov_len = len - (eoh - str) + 1;
582: } else {
583: /* multi part */
584: attr = www_parseAttributes(&ct);
585: if (!attr)
586: return -1;
587: bd.iov_base = bd_begin(www_getAttribute(attr, "boundary"));
588: bd.iov_len = strlen(bd.iov_base);
589: eb = findtextpos(eoh, len - (eoh - str), bd.iov_base, bd.iov_len);
590: free(bd.iov_base);
591:
592: /* set prolog if exists */
593: if (eb != eoh) {
594: m->mime_prolog.iov_base = malloc(eb - eoh + 1);
595: if (!m->mime_prolog.iov_base) {
596: LOGERR;
597: www_freeAttributes(&attr);
598: return -1;
599: }
600: memcpy(m->mime_prolog.iov_base, eoh, eb - eoh);
601: ((char*) m->mime_prolog.iov_base)[eb - eoh] = 0;
602: m->mime_prolog.iov_len = eb - eoh + 1;
603: }
604:
605: m->mime_attach = mime_parseMultiPart(eb + 1, len - (eb + 1 - str),
606: www_getAttribute(attr, "boundary"), &eoh);
607:
608: /* set epilog if exists */
609: if (eoh - str < len) {
610: m->mime_epilog.iov_base = malloc(len - (eoh - str) + 1);
611: if (!m->mime_epilog.iov_base) {
612: LOGERR;
613: www_freeAttributes(&attr);
614: return -1;
615: }
616: memcpy(m->mime_epilog.iov_base, str, len - (eoh - str));
617: ((char*) m->mime_epilog.iov_base)[len - (eoh - str)] = 0;
618: m->mime_epilog.iov_len = len - (eoh - str) + 1;
619:
620: }
621: }
622:
623: www_freeAttributes(&attr);
624: return 0;
625: }
626:
627: /*
628: * mime_calcRawSize() - Calculate estimated memory for data from parsed MIME part
629: *
630: * @m = Mime part
631: * return: -1 error or >-1 data size in mime part
632: */
633: int
634: mime_calcRawSize(struct tagMIME * __restrict m)
635: {
636: const char *s;
637: char *t;
638: int len;
639: register int i;
640:
641: if (!m) {
642: www_SetErr(EINVAL, "Mime part is NULL");
643: return -1;
644: }
645:
646: /* no body */
647: if (m->mime_body.iov_len < 1)
648: return 0;
649:
650: s = mime_getValue(m, "content-transfer-encoding");
651: if (!s)
652: return m->mime_body.iov_len;
653: /* strip whitespaces */
654: while (isspace(*s))
655: s++;
656: t = strchr(s, ';');
657: len = t ? strlen(s) : t - s;
658:
659: /* find proper encoding */
660: for (i = 0; i < sizeof n_encode / sizeof *n_encode; i++)
661: if (len == strlen(n_encode[i]) && !strncasecmp(s, n_encode[i], len))
662: return m->mime_body.iov_len;
663:
664: for (i = 0; i < sizeof encode / sizeof *encode; i++)
665: if (len == strlen(encode[i].name) && !strncasecmp(s, encode[i].name, len))
666: return m->mime_body.iov_len * encode[i].mul;
667:
668: /* fail */
669: return -1;
670: }
671:
672: /*
673: * mime_getRawData() - Get ready parsed data from MIME part body
674: *
675: * @m = Mime part
676: * @str = output data buffer
677: * @len = output data buffer length
678: * return: -1 error or >-1 data length in output buffer
679: */
680: int
681: mime_getRawData(struct tagMIME * __restrict m, char * __restrict str, int slen)
682: {
683: const char *s;
684: char *t;
685: int len;
686: register int i;
687:
688: if (!m || !str) {
689: www_SetErr(EINVAL, "Mime part or string is NULL");
690: return -1;
691: }
692:
693: /* no body */
694: if (m->mime_body.iov_len < 1)
695: return 0;
696:
697: s = mime_getValue(m, "content-transfer-encoding");
698: if (!s) {
699: memcpy(str, m->mime_body.iov_base, m->mime_body.iov_len > (slen - 1) ?
700: slen - 1 : m->mime_body.iov_len);
701: return m->mime_body.iov_len;
702: }
703:
704: /* strip whitespaces */
705: while (isspace(*s))
706: s++;
707: t = strchr(s, ';');
708: len = t ? strlen(s) : t - s;
709:
710: /* decoding body */
711: for (i = 0; i < sizeof encode / sizeof *encode; i++)
712: if (len == strlen(encode[i].name) && !strncasecmp(s, encode[i].name, len))
713: return encode[i].decode(m->mime_body.iov_base,
714: m->mime_body.iov_len, str);
715:
716: /* fail */
717: return -1;
718: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>