Annotation of libaitwww/src/aitwww.c, revision 1.4
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.4 ! misho 6: * $Id: aitwww.c,v 1.3.4.3 2012/07/31 23:08:40 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:
49:
50: #pragma GCC visibility push(hidden)
51:
52: int www_Errno;
53: char www_Error[STRSIZ];
54:
55: #pragma GCC visibility pop
56:
57: // www_GetErrno() Get error code of last operation
58: inline int
59: www_GetErrno()
60: {
61: return www_Errno;
62: }
63:
64: // www_GetError() Get error text of last operation
65: inline const char *
66: www_GetError()
67: {
68: return www_Error;
69: }
70:
71: // www_SetErr() Set error to variables for internal use!!!
72: inline void
73: www_SetErr(int eno, char *estr, ...)
74: {
75: va_list lst;
76:
77: www_Errno = eno;
1.4 ! misho 78: memset(www_Error, 0, sizeof www_Errno);
1.1 misho 79: va_start(lst, estr);
1.4 ! misho 80: vsnprintf(www_Error, sizeof www_Errno, estr, lst);
1.1 misho 81: va_end(lst);
82: }
83:
84: /* -------------------------------------------------------------- */
85:
86: /*
87: * www_initCGI() - Init CGI program
88: *
89: * return: NULL error or allocated cgi session
90: */
91: cgi_t *
92: www_initCGI(void)
93: {
94: char *s, *str;
95: int ctlen, rlen;
96: register int i;
97: cgi_t *cgi = NULL;
98:
99: str = getenv("REQUEST_METHOD");
100: if (!str) {
1.3 misho 101: www_SetErr(EFAULT, "Request method not found");
1.1 misho 102: return NULL;
103: }
104: if (!strcmp(str, "GET") || !strcmp(str, "HEAD")) {
105: /* GET | HEAD */
106: str = getenv("QUERY_STRING");
107: if (!str) {
1.3 misho 108: www_SetErr(EFAULT, "Query string not found");
1.1 misho 109: return NULL;
110: }
111: cgi = www_parseQuery(str);
112: } else if (!strcmp(str, "POST")) {
113: /* POST */
114: str = getenv("CONTENT_LENGTH");
115: if (!str) {
1.3 misho 116: www_SetErr(EFAULT, "Content length not found");
1.1 misho 117: return NULL;
118: } else
119: ctlen = strtol(str, NULL, 0);
120:
121: s = getenv("CONTENT_TYPE");
122: if (!s) {
1.3 misho 123: www_SetErr(EFAULT, "Content type not found");
1.1 misho 124: return NULL;
125: }
126: if (www_cmp(s, "multipart/form-data") &&
127: www_cmp(s, "application/x-www-form-urlencoded")) {
1.3 misho 128: www_SetErr(EFAULT, "MIME parts are broken");
1.1 misho 129: return NULL;
130: }
131:
132: /* allocated space for post data */
1.4 ! misho 133: str = io_malloc(ctlen + 1);
1.1 misho 134: if (!str) {
1.4 ! misho 135: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 136: return NULL;
1.2 misho 137: } else
138: memset(str, 0, ctlen + 1);
1.1 misho 139: for (i = 0; i < ctlen && (rlen =
140: read(STDIN_FILENO, (void*) str + i, ctlen - i)) > 0; i += rlen);
141: str[ctlen] = 0;
142:
143: if (!www_cmp(s, "application/x-www-form-urlencoded"))
144: cgi = www_parseQuery(str);
145: else if (!www_cmp(s, "multipart/form-data"))
146: cgi = www_parseMultiPart(str, ctlen, s);
147:
1.4 ! misho 148: io_free(str);
1.1 misho 149: } else {
150: /* Unknown method */
1.3 misho 151: www_SetErr(EFAULT, "Unknown request method");
1.1 misho 152: return NULL;
153: }
154:
155: return cgi;
156: }
157:
158: /*
159: * www_closeCGI() - Close and free all CGI resources
160: *
161: * @cgi = Inited cgi session
162: * return: none
163: */
164: void
165: www_closeCGI(cgi_t ** __restrict cgi)
166: {
167: struct tagCGI *t;
168:
169: if (!cgi || !*cgi)
170: return;
171:
172: while ((t = SLIST_FIRST(*cgi))) {
1.4 ! misho 173: io_freeVar(&t->cgi_name);
! 174: io_freeVar(&t->cgi_value);
1.1 misho 175:
176: SLIST_REMOVE_HEAD(*cgi, cgi_node);
1.4 ! misho 177: io_free(t);
1.1 misho 178: }
179:
1.4 ! misho 180: io_free(*cgi);
1.1 misho 181: *cgi = NULL;
182: }
183:
184: /*
185: * www_parseQuery() - Parse CGI query string
186: *
187: * @str = String
188: * return: NULL error or allocated cgi session
189: */
190: cgi_t *
191: www_parseQuery(const char *str)
192: {
193: char *base, *wrk;
194: cgi_t *cgi;
195: struct tagCGI *t, *old = NULL;
196:
197: if (!str) {
198: www_SetErr(EINVAL, "String is NULL");
199: return NULL;
200: }
201:
1.4 ! misho 202: cgi = io_malloc(sizeof(cgi_t));
1.1 misho 203: if (!cgi) {
1.4 ! misho 204: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 205: return NULL;
206: } else {
207: memset(cgi, 0, sizeof(cgi_t));
208: SLIST_INIT(cgi);
209: }
210:
1.4 ! misho 211: base = wrk = io_strdup(str);
1.1 misho 212:
213: while (*wrk) {
1.4 ! misho 214: t = io_malloc(sizeof(struct tagCGI));
1.1 misho 215: if (!t) {
1.4 ! misho 216: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 217: www_closeCGI(&cgi);
218: return NULL;
219: } else
220: memset(t, 0, sizeof(struct tagCGI));
221:
222: t->cgi_name = www_getpair(&wrk, "=");
1.4 ! misho 223: www_unescape(AIT_GET_STR(t->cgi_name));
1.1 misho 224:
225: t->cgi_value = www_getpair(&wrk, "&;");
1.4 ! misho 226: www_unescape(AIT_GET_STR(t->cgi_value));
1.1 misho 227:
228: if (!old)
229: SLIST_INSERT_HEAD(cgi, t, cgi_node);
230: else
231: SLIST_INSERT_AFTER(old, t, cgi_node);
232: old = t;
233: }
234:
1.4 ! misho 235: io_free(base);
1.2 misho 236: return cgi;
1.1 misho 237: }
238:
239: /*
240: * www_getValue() - Get Value from CGI session
241: *
242: * @cgi = Inited cgi session
243: * @name = Name of cgi variable
244: * return: NULL not found or !=NULL value
245: */
246: inline const char *
247: www_getValue(cgi_t * __restrict cgi, const char *name)
248: {
249: struct tagCGI *t;
250:
251: if (!cgi || !name) {
252: www_SetErr(EINVAL, "Invalid argument(s)");
253: return NULL;
254: }
255:
256: SLIST_FOREACH(t, cgi, cgi_node)
1.4 ! misho 257: if (t->cgi_name && !strcmp(name, AIT_GET_STR(t->cgi_name)))
! 258: return AIT_GET_STR(t->cgi_value);
1.1 misho 259:
1.2 misho 260: return NULL;
1.1 misho 261: }
262:
263: /*
264: * www_addValue() - Add new or update if exists CGI variable
265: *
266: * @cgi = Inited cgi session
267: * @name = Name of cgi variable
268: * @value = Value of cgi variable
269: * return: -1 error, 0 add new one or 1 updated variable
270: */
271: int
272: www_addValue(cgi_t * __restrict cgi, const char *name, const char *value)
273: {
274: struct tagCGI *t, *tmp;
275:
276: if (!cgi || !name) {
277: www_SetErr(EINVAL, "Invalid argument(s)");
278: return -1;
279: }
280:
281: /* search for update */
282: SLIST_FOREACH_SAFE(t, cgi, cgi_node, tmp) {
1.4 ! misho 283: if (t->cgi_name && !strcmp(name, AIT_GET_STR(t->cgi_name))) {
! 284: AIT_FREE_VAL(t->cgi_value);
! 285: AIT_SET_STR(t->cgi_value, value);
1.1 misho 286: /* update */
287: return 1;
288: }
289: /* save last cgi pair */
290: if (!tmp)
291: break;
292: }
293:
294: /* add new one */
1.4 ! misho 295: tmp = io_malloc(sizeof(struct tagCGI));
1.1 misho 296: if (!tmp) {
1.4 ! misho 297: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 298: return -1;
299: } else
300: memset(tmp, 0, sizeof(struct tagCGI));
301:
1.4 ! misho 302: tmp->cgi_name = io_allocVar();
! 303: if (!tmp->cgi_name) {
! 304: www_SetErr(io_GetErrno(), "%s", io_GetError());
! 305: io_free(tmp);
! 306: return -1;
! 307: } else
! 308: AIT_SET_STR(tmp->cgi_name, name);
! 309: tmp->cgi_value = io_allocVar();
! 310: if (!tmp->cgi_name) {
! 311: www_SetErr(io_GetErrno(), "%s", io_GetError());
! 312: io_freeVar(&tmp->cgi_name);
! 313: io_free(tmp);
! 314: return -1;
! 315: } else
! 316: AIT_SET_STR(tmp->cgi_value, value);
1.1 misho 317:
1.2 misho 318: if (!t)
319: SLIST_INSERT_HEAD(cgi, tmp, cgi_node);
320: else
321: SLIST_INSERT_AFTER(t, tmp, cgi_node);
1.1 misho 322: return 0;
323: }
324:
325: /*
326: * www_delPair() - Delete CGI variable from session
327: *
328: * @cgi = Inited cgi session
329: * @name = Name of cgi variable
330: * return: -1 error, 0 not found or 1 deleted ok
331: */
332: int
333: www_delPair(cgi_t * __restrict cgi, const char *name)
334: {
1.2 misho 335: struct tagCGI *t, *tmp;
1.1 misho 336:
337: if (!cgi || !name) {
338: www_SetErr(EINVAL, "Invalid argument(s)");
339: return -1;
340: }
341:
342: /* search for delete */
1.2 misho 343: SLIST_FOREACH_SAFE(t, cgi, cgi_node, tmp)
1.4 ! misho 344: if (t->cgi_name && !strcmp(name, AIT_GET_STR(t->cgi_name))) {
1.1 misho 345: SLIST_REMOVE(cgi, t, tagCGI, cgi_node);
1.2 misho 346:
1.4 ! misho 347: io_freeVar(&t->cgi_name);
! 348: io_freeVar(&t->cgi_value);
! 349: io_free(t);
1.1 misho 350: return 1;
351: }
352:
353: return 0;
354: }
355:
356: /*
1.2 misho 357: * www_listPairs() - Walk over CGI session variables
358: *
359: * @cgi = Cgi session
360: * @func = If !=NULL call function for each element
361: * @arg = Optional argument pass through callback
362: * return: -1 error or >-1 number of elements
363: */
364: inline int
365: www_listPairs(cgi_t * __restrict cgi, list_cb_t func, void *arg)
366: {
367: register int ret = 0;
368: struct tagCGI *t;
369:
370: if (!cgi) {
371: www_SetErr(EINVAL, "Invalid CGI session argument");
372: return -1;
373: }
374:
375: SLIST_FOREACH(t, cgi, cgi_node) {
376: ret++;
377:
378: if (func)
379: func(t, arg);
380: }
381:
382: return ret;
383: }
384:
385: /*
1.1 misho 386: * www_header() - Output initial html header
387: *
388: * @output = file handle
389: * return: <1 error or >0 writed bytes
390: */
391: inline int
392: www_header(FILE *output)
393: {
394: FILE *f = output ? output : stdout;
395:
396: return fputs("Content-type: text/html\n\n", f);
397: }
398:
1.4 ! misho 399: static ait_val_t *
1.1 misho 400: quotStr(const char *str, const char **end)
401: {
1.4 ! misho 402: char *e;
1.1 misho 403: int n, len = 0;
404: register int i;
1.4 ! misho 405: ait_val_t *s;
1.1 misho 406:
407: /* get str w/o " */
408: if (*str != '"') {
409: n = strspn(str, "!#$%&'*+-.0123456789?ABCDEFGHIJKLMNOPQRSTUVWXYZ"
410: "^_`abcdefghijklmnopqrstuvwxyz{|}~");
1.4 ! misho 411: s = io_allocVar();
1.1 misho 412: if (!s) {
1.4 ! misho 413: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 414: return NULL;
415: } else {
1.4 ! misho 416: AIT_SET_STRSIZ(s, n + 1);
! 417: strlcpy(AIT_GET_STR(s), str, AIT_LEN(s));
1.1 misho 418: *end = str + n;
419: return s;
420: }
421: } else
422: str++;
423: /* get quoted string */
424: if (!(e = strchr(str, '"')))
425: return NULL;
426: else
427: len = e - str;
1.4 ! misho 428:
! 429: s = io_allocVar();
1.1 misho 430: if (!s) {
1.4 ! misho 431: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 432: return NULL;
1.4 ! misho 433: } else {
! 434: AIT_SET_STRSIZ(s, len + 1);
! 435: e = AIT_GET_STR(s);
1.1 misho 436: }
437:
438: for (i = 0; i < len; i++, str++) {
439: if (*str == '\\' || *str == '\n')
1.4 ! misho 440: e[i] = *++str;
1.1 misho 441: else if (*str == '"')
442: break;
443: else
1.4 ! misho 444: e[i] = *str;
1.1 misho 445: }
1.4 ! misho 446: e[i] = 0;
1.1 misho 447:
448: *end = ++str;
449: return s;
450: }
451:
452: static struct tagCGI *
453: addAttr(const char **ct)
454: {
455: struct tagCGI *a;
1.4 ! misho 456: const char *c;
! 457: char *eq;
1.1 misho 458:
459: if (!*ct || !(c = strchr(*ct, ';')))
460: return NULL;
461: else
462: c++;
1.4 ! misho 463: while (isspace((int) *c))
1.1 misho 464: c++;
465:
466: if (!(eq = strchr(c, '=')))
467: return NULL;
468:
1.4 ! misho 469: a = io_malloc(sizeof(struct tagCGI));
! 470: if (!a) {
! 471: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 472: return NULL;
473: }
1.4 ! misho 474: a->cgi_name = io_allocVar();
! 475: if (!a->cgi_name) {
! 476: www_SetErr(io_GetErrno(), "%s", io_GetError());
! 477: io_free(a);
1.1 misho 478: return NULL;
479: }
480:
1.4 ! misho 481: *eq++ = 0;
! 482: AIT_SET_STR(a->cgi_name, c);
! 483: a->cgi_value = quotStr(eq, &c);
! 484: if (!a->cgi_value) {
! 485: io_freeVar(&a->cgi_name);
! 486: io_free(a);
1.1 misho 487: return NULL;
488: }
1.4 ! misho 489:
! 490: *ct = c;
1.1 misho 491: return a;
492: }
493:
494: /*
495: * www_parseMultiPart() - Parse Multi part POST CGI query string
496: *
497: * @str = String
498: * @ctlen = Content length
499: * @ct = Content type
500: * return: NULL error or allocated cgi session
501: */
502: cgi_t *
503: www_parseMultiPart(const char *str, int ctlen, const char *ct)
504: {
505: cgi_t *cgi, *attr;
506: mime_t *mime = NULL;
507: struct tagMIME *m;
508: struct tagCGI *t, *old = NULL;
509: const char *s;
510: int len;
1.4 ! misho 511: ait_val_t *v;
1.1 misho 512:
513: if (!str) {
514: www_SetErr(EINVAL, "String is NULL");
515: return NULL;
516: }
517:
1.4 ! misho 518: cgi = io_malloc(sizeof(cgi_t));
1.1 misho 519: if (!cgi) {
1.4 ! misho 520: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 521: return NULL;
522: } else {
523: memset(cgi, 0, sizeof(cgi_t));
524: SLIST_INIT(cgi);
525: }
526:
527: /* parse MIME messages */
528: attr = www_parseAttributes(&ct);
529: if (!attr) {
530: www_closeCGI(&cgi);
531: return NULL;
532: }
1.4 ! misho 533: v = www_getAttribute(attr, "boundary");
! 534: mime = mime_parseMultiPart(str, ctlen, AIT_GET_STR(v), NULL);
1.1 misho 535: www_freeAttributes(&attr);
1.2 misho 536: if (!mime) {
537: www_closeCGI(&cgi);
538: return NULL;
539: }
1.1 misho 540:
541: SLIST_FOREACH(m, mime, mime_node) {
542: s = mime_getValue(m, "content-disposition");
543: attr = www_parseAttributes(&s);
1.2 misho 544: if (!www_getAttribute(attr, "name")) {
545: www_freeAttributes(&attr);
546: continue;
547: }
1.1 misho 548:
1.4 ! misho 549: t = io_malloc(sizeof(struct tagCGI));
1.1 misho 550: if (!t) {
1.4 ! misho 551: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 552: mime_close(&mime);
553: www_closeCGI(&cgi);
554: return NULL;
555: } else
556: memset(t, 0, sizeof(struct tagCGI));
557:
1.4 ! misho 558: AIT_COPY_VAL(t->cgi_name, www_getAttribute(attr, "name"));
1.1 misho 559: len = mime_calcRawSize(m);
1.4 ! misho 560: t->cgi_value = io_allocVar();
! 561: if (!t->cgi_value) {
! 562: www_SetErr(io_GetErrno(), "%s", io_GetError());
! 563: io_freeVar(&t->cgi_name);
! 564: io_free(t);
! 565: mime_close(&mime);
! 566: www_closeCGI(&cgi);
! 567: return NULL;
! 568: } else {
! 569: AIT_SET_STRSIZ(t->cgi_value, len + 1);
! 570: len = mime_getRawData(m, AIT_GET_STR(t->cgi_value), AIT_LEN(t->cgi_value));
1.1 misho 571: }
572:
573: www_freeAttributes(&attr);
574:
575: if (!old)
576: SLIST_INSERT_HEAD(cgi, t, cgi_node);
577: else
578: SLIST_INSERT_AFTER(old, t, cgi_node);
579: old = t;
580: }
581:
582: mime_close(&mime);
583: return cgi;
584: }
585:
586: /*
587: * www_parseAttributes() - Parse attributes
588: *
589: * @ct = Content type
590: * return: NULL error or !=NULL attributes
591: */
592: inline cgi_t *
593: www_parseAttributes(const char **ct)
594: {
595: struct tagCGI *t, *old = NULL;
596: cgi_t *attr = NULL;
597:
598: if (!ct) {
599: www_SetErr(EINVAL, "String is NULL");
600: return NULL;
601: }
602:
1.4 ! misho 603: attr = io_malloc(sizeof(cgi_t));
1.1 misho 604: if (!attr) {
1.4 ! misho 605: www_SetErr(io_GetErrno(), "%s", io_GetError());
1.1 misho 606: return NULL;
607: } else {
608: memset(attr, 0, sizeof(cgi_t));
609: SLIST_INIT(attr);
610: }
611:
612: /* get mime attributes */
613: while ((t = addAttr(ct))) {
614: if (!old)
615: SLIST_INSERT_HEAD(attr, t, cgi_node);
616: else
617: SLIST_INSERT_AFTER(old, t, cgi_node);
618: old = t;
619: }
620:
621: return attr;
622: }
623:
624: /*
1.4 ! misho 625: * www_getAttribute() - Get Attribute from attribute session
1.1 misho 626: *
1.4 ! misho 627: * @cgi = Inited attribute session
! 628: * @name = Name of attribute variable
! 629: * return: NULL not found or !=NULL value
1.1 misho 630: */
1.4 ! misho 631: inline ait_val_t *
! 632: www_getAttribute(cgi_t * __restrict cgi, const char *name)
1.1 misho 633: {
634: struct tagCGI *t;
635:
1.4 ! misho 636: if (!cgi || !name) {
! 637: www_SetErr(EINVAL, "Invalid argument(s)");
! 638: return NULL;
1.1 misho 639: }
640:
1.4 ! misho 641: SLIST_FOREACH(t, cgi, cgi_node)
! 642: if (t->cgi_name && !strcmp(name, AIT_GET_STR(t->cgi_name)))
1.1 misho 643: return t->cgi_value;
644:
645: return NULL;
646: }
1.4 ! misho 647:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>