Annotation of libaitwww/src/aitwww.c, revision 1.5.6.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.5.6.1 ! misho 6: * $Id: aitwww.c,v 1.5 2013/05/30 09:25:35 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:
1.5.6.1 ! misho 15: Copyright 2004 - 2016
1.1 misho 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
1.5 misho 58: int
1.1 misho 59: www_GetErrno()
60: {
61: return www_Errno;
62: }
63:
64: // www_GetError() Get error text of last operation
1.5 misho 65: const char *
1.1 misho 66: www_GetError()
67: {
68: return www_Error;
69: }
70:
71: // www_SetErr() Set error to variables for internal use!!!
1.5 misho 72: void
1.1 misho 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.5 misho 133: str = e_malloc(ctlen + 1);
1.1 misho 134: if (!str) {
1.5 misho 135: www_SetErr(elwix_GetErrno(), "%s", elwix_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.5 misho 148: e_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.5 misho 173: ait_freeVar(&t->cgi_name);
174: ait_freeVar(&t->cgi_value);
1.1 misho 175:
176: SLIST_REMOVE_HEAD(*cgi, cgi_node);
1.5 misho 177: e_free(t);
1.1 misho 178: }
179:
1.5 misho 180: e_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.5 misho 202: cgi = e_malloc(sizeof(cgi_t));
1.1 misho 203: if (!cgi) {
1.5 misho 204: www_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
1.1 misho 205: return NULL;
206: } else {
207: memset(cgi, 0, sizeof(cgi_t));
208: SLIST_INIT(cgi);
209: }
210:
1.5 misho 211: base = wrk = e_strdup(str);
1.1 misho 212:
213: while (*wrk) {
1.5 misho 214: t = e_malloc(sizeof(struct tagCGI));
1.1 misho 215: if (!t) {
1.5 misho 216: www_SetErr(elwix_GetErrno(), "%s", elwix_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.5 misho 235: e_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: */
1.5 misho 246: const char *
1.1 misho 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.5 misho 295: tmp = e_malloc(sizeof(struct tagCGI));
1.1 misho 296: if (!tmp) {
1.5 misho 297: www_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
1.1 misho 298: return -1;
299: } else
300: memset(tmp, 0, sizeof(struct tagCGI));
301:
1.5 misho 302: tmp->cgi_name = ait_allocVar();
1.4 misho 303: if (!tmp->cgi_name) {
1.5 misho 304: www_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
305: e_free(tmp);
1.4 misho 306: return -1;
307: } else
308: AIT_SET_STR(tmp->cgi_name, name);
1.5 misho 309: tmp->cgi_value = ait_allocVar();
1.4 misho 310: if (!tmp->cgi_name) {
1.5 misho 311: www_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
312: ait_freeVar(&tmp->cgi_name);
313: e_free(tmp);
1.4 misho 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.5 misho 347: ait_freeVar(&t->cgi_name);
348: ait_freeVar(&t->cgi_value);
349: e_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: */
1.5 misho 364: int
1.2 misho 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: */
1.5 misho 391: int
1.1 misho 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.5 misho 411: s = ait_allocVar();
1.1 misho 412: if (!s) {
1.5 misho 413: www_SetErr(elwix_GetErrno(), "%s", elwix_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:
1.5 misho 429: s = ait_allocVar();
1.1 misho 430: if (!s) {
1.5 misho 431: www_SetErr(elwix_GetErrno(), "%s", elwix_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.5 misho 469: a = e_malloc(sizeof(struct tagCGI));
1.4 misho 470: if (!a) {
1.5 misho 471: www_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
1.1 misho 472: return NULL;
473: }
1.5 misho 474: a->cgi_name = ait_allocVar();
1.4 misho 475: if (!a->cgi_name) {
1.5 misho 476: www_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
477: e_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) {
1.5 misho 485: ait_freeVar(&a->cgi_name);
486: e_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.5 misho 518: cgi = e_malloc(sizeof(cgi_t));
1.1 misho 519: if (!cgi) {
1.5 misho 520: www_SetErr(elwix_GetErrno(), "%s", elwix_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.5 misho 549: t = e_malloc(sizeof(struct tagCGI));
1.1 misho 550: if (!t) {
1.5 misho 551: www_SetErr(elwix_GetErrno(), "%s", elwix_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.5 misho 560: t->cgi_value = ait_allocVar();
1.4 misho 561: if (!t->cgi_value) {
1.5 misho 562: www_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
563: ait_freeVar(&t->cgi_name);
564: e_free(t);
1.4 misho 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: */
1.5 misho 592: cgi_t *
1.1 misho 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.5 misho 603: attr = e_malloc(sizeof(cgi_t));
1.1 misho 604: if (!attr) {
1.5 misho 605: www_SetErr(elwix_GetErrno(), "%s", elwix_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.5 misho 631: ait_val_t *
1.4 misho 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>