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