Annotation of libaitwww/src/mime.c, revision 1.1.1.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 $
6: * $Id: array.c,v 1.7 2012/02/02 21:32:42 misho Exp $
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;
416: len -= bd[0].iov_len;
417:
418: if (end)
419: *end = str;
420: return mime;
421: }
422:
423: static inline void
424: freeMIME(struct tagMIME * __restrict m)
425: {
426: if (m->mime_body.iov_base)
427: free(m->mime_body.iov_base);
428: if (m->mime_prolog.iov_base)
429: free(m->mime_prolog.iov_base);
430: if (m->mime_epilog.iov_base)
431: free(m->mime_epilog.iov_base);
432:
433: freeHeader(m);
434: mime_close(&m->mime_attach);
435: }
436:
437: /*
438: * mime_close() - Close MIME session and free all resources
439: *
440: * @mime = Inited mime session
441: * return: none
442: */
443: void
444: mime_close(mime_t ** __restrict mime)
445: {
446: struct tagMIME *m;
447:
448: if (!mime || !*mime)
449: return;
450:
451: while ((m = SLIST_FIRST(*mime))) {
452: SLIST_REMOVE_HEAD(*mime, mime_node);
453: freeMIME(m);
454: free(m);
455: }
456:
457: free(*mime);
458: *mime = NULL;
459: }
460:
461: /*
462: * mime_parseHeader() - Parse MIME header pairs
463: *
464: * @m = Mime part
465: * @str = String
466: * @len = String length
467: * @end = End of parsed part
468: * return: -1 error or 0 ok
469: */
470: int
471: mime_parseHeader(struct tagMIME * __restrict m, const char *str, size_t len, const char **end)
472: {
473: const char *e, *colon, *eoh;
474: struct tagCGI *c, *old = NULL;
475:
476: if (!m || !str) {
477: www_SetErr(EINVAL, "Mime part or string is NULL");
478: return -1;
479: } else
480: e = str + len;
481:
482: while (str < e) {
483: if (!memcmp(str, CRLF, strlen(CRLF))) {
484: str += 2;
485: break;
486: }
487:
488: colon = memchr(str, ':', e - str);
489: eoh = findtextpos(str, e - str, CRLF, strlen(CRLF));
490: if (!colon || !eoh || colon > eoh) {
491: www_SetErr(EBADMSG, "Bad MIME format message");
492: freeHeader(m);
493: return -1;
494: }
495:
496: c = malloc(sizeof(struct tagCGI));
497: if (!c) {
498: LOGERR;
499: freeHeader(m);
500: return -1;
501: }
502: /* get name */
503: c->cgi_name = malloc(colon - str + 1);
504: if (!c->cgi_name) {
505: LOGERR;
506: free(c);
507: freeHeader(m);
508: return -1;
509: } else {
510: memcpy(c->cgi_name, str, colon - str);
511: c->cgi_name[colon - str] = 0;
512: }
513: /* get value */
514: c->cgi_value = hdrValue(colon + 1, e - colon - 1, &str);
515:
516: if (!old)
517: SLIST_INSERT_HEAD(&m->mime_header, c, cgi_node);
518: else
519: SLIST_INSERT_AFTER(old, c, cgi_node);
520: old = c;
521: }
522:
523: if (end)
524: *end = str;
525: return 0;
526: }
527:
528: /*
529: * mime_getValue() - Get value from MIME header
530: *
531: * @m = Mime part
532: * @name = Header name
533: * return: NULL not found or !=NULL value
534: */
535: inline const char *
536: mime_getValue(struct tagMIME * __restrict m, const char *name)
537: {
538: struct tagCGI *c;
539: const char *v = NULL;
540:
541: SLIST_FOREACH(c, &m->mime_header, cgi_node)
542: if (!strcmp(c->cgi_name, name)) {
543: v = c->cgi_value;
544: break;
545: }
546: return v;
547: }
548:
549: /*
550: * mime_readPart() Read and parse MIME part
551: *
552: * @m = Mime part
553: * @str = String
554: * @len = String length
555: * return: -1 error or 0 ok
556: */
557: int
558: mime_readPart(struct tagMIME * __restrict m, const char *str, size_t len)
559: {
560: const char *eoh, *ct, *eb;
561: cgi_t *attr;
562: struct iovec bd;
563:
564: if (!m || !str) {
565: www_SetErr(EINVAL, "Mime part or string is NULL");
566: return -1;
567: }
568:
569: if (mime_parseHeader(m, str, len, &eoh))
570: return -1;
571:
572: ct = mime_getValue(m, "content-type");
573: if (!ct || www_cmptype(ct, "multipart")) {
574: /* not multi part, assign like body element */
575: m->mime_body.iov_base = malloc(len - (eoh - str) + 1);
576: memcpy(m->mime_body.iov_base, eoh, len - (eoh - str));
577: ((char*) m->mime_body.iov_base)[len - (eoh - str)] = 0;
578: m->mime_body.iov_len = len - (eoh - str) + 1;
579: } else {
580: /* multi part */
581: attr = www_parseAttributes(&ct);
582: if (!attr)
583: return -1;
584: bd.iov_base = bd_begin(www_getAttribute(attr, "boundary"));
585: bd.iov_len = strlen(bd.iov_base);
586: eb = findtextpos(eoh, len - (eoh - str), bd.iov_base, bd.iov_len);
587: free(bd.iov_base);
588:
589: /* set prolog if exists */
590: if (eb != eoh) {
591: m->mime_prolog.iov_base = malloc(eb - eoh + 1);
592: if (!m->mime_prolog.iov_base) {
593: LOGERR;
594: www_freeAttributes(&attr);
595: return -1;
596: }
597: memcpy(m->mime_prolog.iov_base, eoh, eb - eoh);
598: ((char*) m->mime_prolog.iov_base)[eb - eoh] = 0;
599: m->mime_prolog.iov_len = eb - eoh + 1;
600: }
601:
602: m->mime_attach = mime_parseMultiPart(eb + 1, len - (eb + 1 - str),
603: www_getAttribute(attr, "boundary"), &eoh);
604:
605: /* set epilog if exists */
606: if (eoh - str < len) {
607: m->mime_epilog.iov_base = malloc(len - (eoh - str) + 1);
608: if (!m->mime_epilog.iov_base) {
609: LOGERR;
610: www_freeAttributes(&attr);
611: return -1;
612: }
613: memcpy(m->mime_epilog.iov_base, str, len - (eoh - str));
614: ((char*) m->mime_epilog.iov_base)[len - (eoh - str)] = 0;
615: m->mime_epilog.iov_len = len - (eoh - str) + 1;
616:
617: }
618: }
619:
620: www_freeAttributes(&attr);
621: return 0;
622: }
623:
624: /*
625: * mime_calcRawSize() - Calculate estimated memory for data from parsed MIME part
626: *
627: * @m = Mime part
628: * return: -1 error or >-1 data size in mime part
629: */
630: int
631: mime_calcRawSize(struct tagMIME * __restrict m)
632: {
633: const char *s;
634: char *t;
635: int len;
636: register int i;
637:
638: if (!m) {
639: www_SetErr(EINVAL, "Mime part is NULL");
640: return -1;
641: }
642:
643: /* no body */
644: if (m->mime_body.iov_len < 1)
645: return 0;
646:
647: s = mime_getValue(m, "content-transfer-encoding");
648: if (!s)
649: return m->mime_body.iov_len;
650: /* strip whitespaces */
651: while (isspace(*s))
652: s++;
653: t = strchr(s, ';');
654: len = t ? strlen(s) : t - s;
655:
656: /* find proper encoding */
657: for (i = 0; i < sizeof n_encode / sizeof *n_encode; i++)
658: if (len == strlen(n_encode[i]) && !strncasecmp(s, n_encode[i], len))
659: return m->mime_body.iov_len;
660:
661: for (i = 0; i < sizeof encode / sizeof *encode; i++)
662: if (len == strlen(encode[i].name) && !strncasecmp(s, encode[i].name, len))
663: return m->mime_body.iov_len * encode[i].mul;
664:
665: /* fail */
666: return -1;
667: }
668:
669: /*
670: * mime_getRawData() - Get ready parsed data from MIME part body
671: *
672: * @m = Mime part
673: * @str = output data buffer
674: * @len = output data buffer length
675: * return: -1 error or >-1 data length in output buffer
676: */
677: int
678: mime_getRawData(struct tagMIME * __restrict m, char * __restrict str, int slen)
679: {
680: const char *s;
681: char *t;
682: int len;
683: register int i;
684:
685: if (!m || !str) {
686: www_SetErr(EINVAL, "Mime part or string is NULL");
687: return -1;
688: }
689:
690: /* no body */
691: if (m->mime_body.iov_len < 1)
692: return 0;
693:
694: s = mime_getValue(m, "content-transfer-encoding");
695: if (!s) {
696: memcpy(str, m->mime_body.iov_base, m->mime_body.iov_len > (slen - 1) ?
697: slen - 1 : m->mime_body.iov_len);
698: return m->mime_body.iov_len;
699: }
700:
701: /* strip whitespaces */
702: while (isspace(*s))
703: s++;
704: t = strchr(s, ';');
705: len = t ? strlen(s) : t - s;
706:
707: /* decoding body */
708: for (i = 0; i < sizeof encode / sizeof *encode; i++)
709: if (len == strlen(encode[i].name) && !strncasecmp(s, encode[i].name, len))
710: return encode[i].decode(m->mime_body.iov_base,
711: m->mime_body.iov_len, str);
712:
713: /* fail */
714: return -1;
715: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>