Annotation of libaitwww/src/aitwww.c, revision 1.1.1.1.2.8

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.8! misho       6: * $Id: aitwww.c,v 1.1.1.1.2.7 2012/03/09 16:42:31 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;
1.1.1.1.2.5  misho     138:                } else
                    139:                        memset(str, 0, ctlen + 1);
1.1.1.1.2.8! misho     140:                for (i = 0; i < ctlen && (rlen = 
1.1       misho     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 */
                    152:                www_SetErr(EBADMSG, "Unknown request method");
                    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.1.1.1.2.1  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.1.1.1.2.1  misho     261:                        return t->cgi_value;
1.1       misho     262: 
1.1.1.1.2.1  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.1.1.1.2.1  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.1.1.1.2.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.1.1.1.2.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.1.1.1.2.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.1.1.1.2.3  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.1.1.1.2.4  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.1.1.1.2.6  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>