Annotation of libaitwww/src/aitwww.c, revision 1.1.1.1.2.2
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.1.1.1.2.2! misho 6: * $Id: aitwww.c,v 1.1.1.1.2.1 2012/03/09 09:38:55 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) {
102: www_SetErr(EBADMSG, "Request method not found");
103: return NULL;
104: }
105: if (!strcmp(str, "GET") || !strcmp(str, "HEAD")) {
106: /* GET | HEAD */
107: str = getenv("QUERY_STRING");
108: if (!str) {
109: www_SetErr(EBADMSG, "Query string not found");
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) {
117: www_SetErr(EBADMSG, "Content length not found");
118: return NULL;
119: } else
120: ctlen = strtol(str, NULL, 0);
121:
122: s = getenv("CONTENT_TYPE");
123: if (!s) {
124: www_SetErr(EBADMSG, "Content type not found");
125: return NULL;
126: }
127: if (www_cmp(s, "multipart/form-data") &&
128: www_cmp(s, "application/x-www-form-urlencoded")) {
129: www_SetErr(EBADMSG, "MIME parts are broken");
130: return NULL;
131: }
132:
133: /* allocated space for post data */
134: str = malloc(ctlen + 1);
135: if (!str) {
136: LOGERR;
137: return NULL;
138: }
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 */
151: www_SetErr(EBADMSG, "Unknown request method");
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.1.1.1.2.1 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.1.1.1.2.1 misho 260: return t->cgi_value;
1.1 misho 261:
1.1.1.1.2.1 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.1.1.1.2.1 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.1.1.1.2.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.1.1.1.2.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.1.1.1.2.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: /*
351: * www_header() - Output initial html header
352: *
353: * @output = file handle
354: * return: <1 error or >0 writed bytes
355: */
356: inline int
357: www_header(FILE *output)
358: {
359: FILE *f = output ? output : stdout;
360:
361: return fputs("Content-type: text/html\n\n", f);
362: }
363:
364: static char *
365: quotStr(const char *str, const char **end)
366: {
367: char *s, *e;
368: int n, len = 0;
369: register int i;
370:
371: /* get str w/o " */
372: if (*str != '"') {
373: n = strspn(str, "!#$%&'*+-.0123456789?ABCDEFGHIJKLMNOPQRSTUVWXYZ"
374: "^_`abcdefghijklmnopqrstuvwxyz{|}~");
375: s = malloc(n + 1);
376: if (!s) {
377: LOGERR;
378: return NULL;
379: } else {
380: strncpy(s, str, n);
381: s[n] = 0;
382: *end = str + n;
383: return s;
384: }
385: } else
386: str++;
387: /* get quoted string */
388: if (!(e = strchr(str, '"')))
389: return NULL;
390: else
391: len = e - str;
392: s = malloc(len + 1);
393: if (!s) {
394: LOGERR;
395: return NULL;
396: }
397:
398: for (i = 0; i < len; i++, str++) {
399: if (*str == '\\' || *str == '\n')
400: s[i] = *++str;
401: else if (*str == '"')
402: break;
403: else
404: s[i] = *str;
405: }
406: s[i] = 0;
407:
408: *end = ++str;
409: return s;
410: }
411:
412: static struct tagCGI *
413: addAttr(const char **ct)
414: {
415: struct tagCGI *a;
416: const char *c, *eq;
417: char *name, *value;
418:
419: if (!*ct || !(c = strchr(*ct, ';')))
420: return NULL;
421: else
422: c++;
423: while (isspace(*c))
424: c++;
425:
426: if (!(eq = strchr(c, '=')))
427: return NULL;
428:
429: /* parse name */
430: name = malloc(eq - c + 1);
431: if (!name) {
432: LOGERR;
433: return NULL;
434: } else {
435: strncpy(name, c, eq - c);
436: name[eq - c] = 0;
437: }
438: /* parse value */
439: value = quotStr(++eq, &c);
440: if (!value) {
441: free(name);
442: return NULL;
443: }
444:
445: /* fill tagCGI */
446: a = malloc(sizeof(struct tagCGI));
447: if (!a) {
448: LOGERR;
449: return NULL;
450: } else {
451: a->cgi_name = name;
452: a->cgi_value = value;
453: *ct = c;
454: }
455: return a;
456: }
457:
458: /*
459: * www_parseMultiPart() - Parse Multi part POST CGI query string
460: *
461: * @str = String
462: * @ctlen = Content length
463: * @ct = Content type
464: * return: NULL error or allocated cgi session
465: */
466: cgi_t *
467: www_parseMultiPart(const char *str, int ctlen, const char *ct)
468: {
469: cgi_t *cgi, *attr;
470: mime_t *mime = NULL;
471: struct tagMIME *m;
472: struct tagCGI *t, *old = NULL;
473: const char *s;
474: int len;
475:
476: if (!str) {
477: www_SetErr(EINVAL, "String is NULL");
478: return NULL;
479: }
480:
481: cgi = malloc(sizeof(cgi_t));
482: if (!cgi) {
483: LOGERR;
484: return NULL;
485: } else {
486: memset(cgi, 0, sizeof(cgi_t));
487: SLIST_INIT(cgi);
488: }
489:
490: /* parse MIME messages */
491: attr = www_parseAttributes(&ct);
492: if (!attr) {
493: www_closeCGI(&cgi);
494: return NULL;
495: }
496: mime = mime_parseMultiPart(str, ctlen, www_getAttribute(attr, "boundary"), NULL);
497: www_freeAttributes(&attr);
498:
499: SLIST_FOREACH(m, mime, mime_node) {
500: s = mime_getValue(m, "content-disposition");
501: attr = www_parseAttributes(&s);
502:
503: t = malloc(sizeof(struct tagCGI));
504: if (!t) {
505: LOGERR;
506: mime_close(&mime);
507: www_closeCGI(&cgi);
508: return NULL;
509: } else
510: memset(t, 0, sizeof(struct tagCGI));
511:
512: t->cgi_name = strdup(www_getAttribute(attr, "name"));
513: len = mime_calcRawSize(m);
514: t->cgi_value = malloc(len + 1);
515: if (t->cgi_value) {
516: len = mime_getRawData(m, t->cgi_value, len + 1);
517: t->cgi_value[len] = 0;
518: }
519:
520: www_freeAttributes(&attr);
521:
522: if (!old)
523: SLIST_INSERT_HEAD(cgi, t, cgi_node);
524: else
525: SLIST_INSERT_AFTER(old, t, cgi_node);
526: old = t;
527: }
528:
529: mime_close(&mime);
530: return cgi;
531: }
532:
533: /*
534: * www_parseAttributes() - Parse attributes
535: *
536: * @ct = Content type
537: * return: NULL error or !=NULL attributes
538: */
539: inline cgi_t *
540: www_parseAttributes(const char **ct)
541: {
542: struct tagCGI *t, *old = NULL;
543: cgi_t *attr = NULL;
544:
545: if (!ct) {
546: www_SetErr(EINVAL, "String is NULL");
547: return NULL;
548: }
549:
550: attr = malloc(sizeof(cgi_t));
551: if (!attr) {
552: LOGERR;
553: return NULL;
554: } else {
555: memset(attr, 0, sizeof(cgi_t));
556: SLIST_INIT(attr);
557: }
558:
559: /* get mime attributes */
560: while ((t = addAttr(ct))) {
561: if (!old)
562: SLIST_INSERT_HEAD(attr, t, cgi_node);
563: else
564: SLIST_INSERT_AFTER(old, t, cgi_node);
565: old = t;
566: }
567:
568: return attr;
569: }
570:
571: /*
572: * www_freeAttributes() - Free attributes
573: *
574: * @attr = Attributes
575: * return: none
576: */
577: inline void
578: www_freeAttributes(cgi_t ** __restrict attr)
579: {
580: struct tagCGI *t;
581:
582: if (!attr || !*attr)
583: return;
584:
585: /* free mime attributes */
586: while ((t = SLIST_FIRST(*attr))) {
587: if (t->cgi_name)
588: free(t->cgi_name);
589: if (t->cgi_value)
590: free(t->cgi_value);
591: SLIST_REMOVE_HEAD(*attr, cgi_node);
592: free(t);
593: }
594:
595: free(*attr);
596: *attr = NULL;
597: }
598:
599: /*
600: * www_getAttribute() - Get attribute by name
601: *
602: * @attr = Attributes
603: * @name = Name of attribute
604: * return: NULL not found or !=NULL attribute value
605: */
606: inline const char *
607: www_getAttribute(cgi_t * __restrict attr, const char *name)
608: {
609: struct tagCGI *t;
610:
611: SLIST_FOREACH(t, attr, cgi_node)
612: if (!strcasecmp(t->cgi_name, name))
613: return t->cgi_value;
614:
615: return NULL;
616: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>