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