Annotation of libaitwww/src/aitwww.c, revision 1.3.4.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.3.4.1 ! misho 6: * $Id: aitwww.c,v 1.3 2012/03/15 01:59:37 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.3.4.1 ! misho 78: memset(www_Error, 0, sizeof www_Errno);
1.1 misho 79: va_start(lst, estr);
1.3.4.1 ! 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 */
133: str = malloc(ctlen + 1);
134: if (!str) {
135: LOGERR;
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:
148: free(str);
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))) {
173: if (t->cgi_name)
174: free(t->cgi_name);
175: if (t->cgi_value)
176: free(t->cgi_value);
177:
178: SLIST_REMOVE_HEAD(*cgi, cgi_node);
179: free(t);
180: }
181:
182: free(*cgi);
183: *cgi = NULL;
184: }
185:
186: /*
187: * www_parseQuery() - Parse CGI query string
188: *
189: * @str = String
190: * return: NULL error or allocated cgi session
191: */
192: cgi_t *
193: www_parseQuery(const char *str)
194: {
195: char *base, *wrk;
196: cgi_t *cgi;
197: struct tagCGI *t, *old = NULL;
198:
199: if (!str) {
200: www_SetErr(EINVAL, "String is NULL");
201: return NULL;
202: }
203:
204: cgi = malloc(sizeof(cgi_t));
205: if (!cgi) {
206: LOGERR;
207: return NULL;
208: } else {
209: memset(cgi, 0, sizeof(cgi_t));
210: SLIST_INIT(cgi);
211: }
212:
213: base = wrk = strdup(str);
214:
215: while (*wrk) {
216: t = malloc(sizeof(struct tagCGI));
217: if (!t) {
218: LOGERR;
219: www_closeCGI(&cgi);
220: return NULL;
221: } else
222: memset(t, 0, sizeof(struct tagCGI));
223:
224: t->cgi_name = www_getpair(&wrk, "=");
225: www_unescape(t->cgi_name);
226:
227: t->cgi_value = www_getpair(&wrk, "&;");
228: www_unescape(t->cgi_value);
229:
230: if (!old)
231: SLIST_INSERT_HEAD(cgi, t, cgi_node);
232: else
233: SLIST_INSERT_AFTER(old, t, cgi_node);
234: old = t;
235: }
236:
237: free(base);
1.2 misho 238: return cgi;
1.1 misho 239: }
240:
241: /*
242: * www_getValue() - Get Value from CGI session
243: *
244: * @cgi = Inited cgi session
245: * @name = Name of cgi variable
246: * return: NULL not found or !=NULL value
247: */
248: inline const char *
249: www_getValue(cgi_t * __restrict cgi, const char *name)
250: {
251: struct tagCGI *t;
252:
253: if (!cgi || !name) {
254: www_SetErr(EINVAL, "Invalid argument(s)");
255: return NULL;
256: }
257:
258: SLIST_FOREACH(t, cgi, cgi_node)
259: if (t->cgi_name && !strcmp(name, t->cgi_name))
1.2 misho 260: return t->cgi_value;
1.1 misho 261:
1.2 misho 262: return NULL;
1.1 misho 263: }
264:
265: /*
266: * www_addValue() - Add new or update if exists CGI variable
267: *
268: * @cgi = Inited cgi session
269: * @name = Name of cgi variable
270: * @value = Value of cgi variable
271: * return: -1 error, 0 add new one or 1 updated variable
272: */
273: int
274: www_addValue(cgi_t * __restrict cgi, const char *name, const char *value)
275: {
276: struct tagCGI *t, *tmp;
277:
278: if (!cgi || !name) {
279: www_SetErr(EINVAL, "Invalid argument(s)");
280: return -1;
281: }
282:
283: /* search for update */
284: SLIST_FOREACH_SAFE(t, cgi, cgi_node, tmp) {
285: if (t->cgi_name && !strcmp(name, t->cgi_name)) {
286: if (t->cgi_value)
287: free(t->cgi_value);
288: if (value)
289: t->cgi_value = strdup(value);
290: /* update */
291: return 1;
292: }
293: /* save last cgi pair */
294: if (!tmp)
295: break;
296: }
297:
298: /* add new one */
299: tmp = malloc(sizeof(struct tagCGI));
300: if (!tmp) {
301: LOGERR;
302: return -1;
303: } else
304: memset(tmp, 0, sizeof(struct tagCGI));
305:
306: tmp->cgi_name = strdup(name);
307: if (value)
308: tmp->cgi_value = strdup(value);
309:
1.2 misho 310: if (!t)
311: SLIST_INSERT_HEAD(cgi, tmp, cgi_node);
312: else
313: SLIST_INSERT_AFTER(t, tmp, cgi_node);
1.1 misho 314: return 0;
315: }
316:
317: /*
318: * www_delPair() - Delete CGI variable from session
319: *
320: * @cgi = Inited cgi session
321: * @name = Name of cgi variable
322: * return: -1 error, 0 not found or 1 deleted ok
323: */
324: int
325: www_delPair(cgi_t * __restrict cgi, const char *name)
326: {
1.2 misho 327: struct tagCGI *t, *tmp;
1.1 misho 328:
329: if (!cgi || !name) {
330: www_SetErr(EINVAL, "Invalid argument(s)");
331: return -1;
332: }
333:
334: /* search for delete */
1.2 misho 335: SLIST_FOREACH_SAFE(t, cgi, cgi_node, tmp)
1.1 misho 336: if (t->cgi_name && !strcmp(name, t->cgi_name)) {
337: SLIST_REMOVE(cgi, t, tagCGI, cgi_node);
1.2 misho 338:
339: if (t->cgi_name)
340: free(t->cgi_name);
341: if (t->cgi_value)
342: free(t->cgi_value);
343: free(t);
1.1 misho 344: return 1;
345: }
346:
347: return 0;
348: }
349:
350: /*
1.2 misho 351: * www_listPairs() - Walk over CGI session variables
352: *
353: * @cgi = Cgi session
354: * @func = If !=NULL call function for each element
355: * @arg = Optional argument pass through callback
356: * return: -1 error or >-1 number of elements
357: */
358: inline int
359: www_listPairs(cgi_t * __restrict cgi, list_cb_t func, void *arg)
360: {
361: register int ret = 0;
362: struct tagCGI *t;
363:
364: if (!cgi) {
365: www_SetErr(EINVAL, "Invalid CGI session argument");
366: return -1;
367: }
368:
369: SLIST_FOREACH(t, cgi, cgi_node) {
370: ret++;
371:
372: if (func)
373: func(t, arg);
374: }
375:
376: return ret;
377: }
378:
379: /*
1.1 misho 380: * www_header() - Output initial html header
381: *
382: * @output = file handle
383: * return: <1 error or >0 writed bytes
384: */
385: inline int
386: www_header(FILE *output)
387: {
388: FILE *f = output ? output : stdout;
389:
390: return fputs("Content-type: text/html\n\n", f);
391: }
392:
393: static char *
394: quotStr(const char *str, const char **end)
395: {
396: char *s, *e;
397: int n, len = 0;
398: register int i;
399:
400: /* get str w/o " */
401: if (*str != '"') {
402: n = strspn(str, "!#$%&'*+-.0123456789?ABCDEFGHIJKLMNOPQRSTUVWXYZ"
403: "^_`abcdefghijklmnopqrstuvwxyz{|}~");
404: s = malloc(n + 1);
405: if (!s) {
406: LOGERR;
407: return NULL;
408: } else {
409: strncpy(s, str, n);
410: s[n] = 0;
411: *end = str + n;
412: return s;
413: }
414: } else
415: str++;
416: /* get quoted string */
417: if (!(e = strchr(str, '"')))
418: return NULL;
419: else
420: len = e - str;
421: s = malloc(len + 1);
422: if (!s) {
423: LOGERR;
424: return NULL;
425: }
426:
427: for (i = 0; i < len; i++, str++) {
428: if (*str == '\\' || *str == '\n')
429: s[i] = *++str;
430: else if (*str == '"')
431: break;
432: else
433: s[i] = *str;
434: }
435: s[i] = 0;
436:
437: *end = ++str;
438: return s;
439: }
440:
441: static struct tagCGI *
442: addAttr(const char **ct)
443: {
444: struct tagCGI *a;
445: const char *c, *eq;
446: char *name, *value;
447:
448: if (!*ct || !(c = strchr(*ct, ';')))
449: return NULL;
450: else
451: c++;
452: while (isspace(*c))
453: c++;
454:
455: if (!(eq = strchr(c, '=')))
456: return NULL;
457:
458: /* parse name */
459: name = malloc(eq - c + 1);
460: if (!name) {
461: LOGERR;
462: return NULL;
463: } else {
464: strncpy(name, c, eq - c);
465: name[eq - c] = 0;
466: }
467: /* parse value */
468: value = quotStr(++eq, &c);
469: if (!value) {
470: free(name);
471: return NULL;
472: }
473:
474: /* fill tagCGI */
475: a = malloc(sizeof(struct tagCGI));
476: if (!a) {
477: LOGERR;
478: return NULL;
479: } else {
480: a->cgi_name = name;
481: a->cgi_value = value;
482: *ct = c;
483: }
484: return a;
485: }
486:
487: /*
488: * www_parseMultiPart() - Parse Multi part POST CGI query string
489: *
490: * @str = String
491: * @ctlen = Content length
492: * @ct = Content type
493: * return: NULL error or allocated cgi session
494: */
495: cgi_t *
496: www_parseMultiPart(const char *str, int ctlen, const char *ct)
497: {
498: cgi_t *cgi, *attr;
499: mime_t *mime = NULL;
500: struct tagMIME *m;
501: struct tagCGI *t, *old = NULL;
502: const char *s;
503: int len;
504:
505: if (!str) {
506: www_SetErr(EINVAL, "String is NULL");
507: return NULL;
508: }
509:
510: cgi = malloc(sizeof(cgi_t));
511: if (!cgi) {
512: LOGERR;
513: return NULL;
514: } else {
515: memset(cgi, 0, sizeof(cgi_t));
516: SLIST_INIT(cgi);
517: }
518:
519: /* parse MIME messages */
520: attr = www_parseAttributes(&ct);
521: if (!attr) {
522: www_closeCGI(&cgi);
523: return NULL;
524: }
525: mime = mime_parseMultiPart(str, ctlen, www_getAttribute(attr, "boundary"), NULL);
526: www_freeAttributes(&attr);
1.2 misho 527: if (!mime) {
528: www_closeCGI(&cgi);
529: return NULL;
530: }
1.1 misho 531:
532: SLIST_FOREACH(m, mime, mime_node) {
533: s = mime_getValue(m, "content-disposition");
534: attr = www_parseAttributes(&s);
1.2 misho 535: if (!www_getAttribute(attr, "name")) {
536: www_freeAttributes(&attr);
537: continue;
538: }
1.1 misho 539:
540: t = malloc(sizeof(struct tagCGI));
541: if (!t) {
542: LOGERR;
543: mime_close(&mime);
544: www_closeCGI(&cgi);
545: return NULL;
546: } else
547: memset(t, 0, sizeof(struct tagCGI));
548:
549: t->cgi_name = strdup(www_getAttribute(attr, "name"));
550: len = mime_calcRawSize(m);
551: t->cgi_value = malloc(len + 1);
552: if (t->cgi_value) {
553: len = mime_getRawData(m, t->cgi_value, len + 1);
554: t->cgi_value[len] = 0;
555: }
556:
557: www_freeAttributes(&attr);
558:
559: if (!old)
560: SLIST_INSERT_HEAD(cgi, t, cgi_node);
561: else
562: SLIST_INSERT_AFTER(old, t, cgi_node);
563: old = t;
564: }
565:
566: mime_close(&mime);
567: return cgi;
568: }
569:
570: /*
571: * www_parseAttributes() - Parse attributes
572: *
573: * @ct = Content type
574: * return: NULL error or !=NULL attributes
575: */
576: inline cgi_t *
577: www_parseAttributes(const char **ct)
578: {
579: struct tagCGI *t, *old = NULL;
580: cgi_t *attr = NULL;
581:
582: if (!ct) {
583: www_SetErr(EINVAL, "String is NULL");
584: return NULL;
585: }
586:
587: attr = malloc(sizeof(cgi_t));
588: if (!attr) {
589: LOGERR;
590: return NULL;
591: } else {
592: memset(attr, 0, sizeof(cgi_t));
593: SLIST_INIT(attr);
594: }
595:
596: /* get mime attributes */
597: while ((t = addAttr(ct))) {
598: if (!old)
599: SLIST_INSERT_HEAD(attr, t, cgi_node);
600: else
601: SLIST_INSERT_AFTER(old, t, cgi_node);
602: old = t;
603: }
604:
605: return attr;
606: }
607:
608: /*
609: * www_freeAttributes() - Free attributes
610: *
611: * @attr = Attributes
612: * return: none
613: */
614: inline void
615: www_freeAttributes(cgi_t ** __restrict attr)
616: {
617: struct tagCGI *t;
618:
619: if (!attr || !*attr)
620: return;
621:
622: /* free mime attributes */
623: while ((t = SLIST_FIRST(*attr))) {
624: if (t->cgi_name)
625: free(t->cgi_name);
626: if (t->cgi_value)
627: free(t->cgi_value);
628: SLIST_REMOVE_HEAD(*attr, cgi_node);
629: free(t);
630: }
631:
632: free(*attr);
633: *attr = NULL;
634: }
635:
636: /*
637: * www_getAttribute() - Get attribute by name
638: *
639: * @attr = Attributes
640: * @name = Name of attribute
641: * return: NULL not found or !=NULL attribute value
642: */
643: inline const char *
644: www_getAttribute(cgi_t * __restrict attr, const char *name)
645: {
646: struct tagCGI *t;
647:
648: SLIST_FOREACH(t, attr, cgi_node)
649: if (!strcasecmp(t->cgi_name, name))
650: return t->cgi_value;
651:
652: return NULL;
653: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>