Annotation of embedaddon/libpdel/http/servlet/http_servlet_tmpl.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (c) 2001-2002 Packet Design, LLC.
                      4:  * All rights reserved.
                      5:  * 
                      6:  * Subject to the following obligations and disclaimer of warranty,
                      7:  * use and redistribution of this software, in source or object code
                      8:  * forms, with or without modifications are expressly permitted by
                      9:  * Packet Design; provided, however, that:
                     10:  * 
                     11:  *    (i)  Any and all reproductions of the source or object code
                     12:  *         must include the copyright notice above and the following
                     13:  *         disclaimer of warranties; and
                     14:  *    (ii) No rights are granted, in any manner or form, to use
                     15:  *         Packet Design trademarks, including the mark "PACKET DESIGN"
                     16:  *         on advertising, endorsements, or otherwise except as such
                     17:  *         appears in the above copyright notice or in the software.
                     18:  * 
                     19:  * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
                     20:  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
                     21:  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
                     22:  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
                     23:  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
                     24:  * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
                     25:  * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
                     26:  * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
                     27:  * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
                     28:  * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
                     29:  * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
                     30:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
                     31:  * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
                     32:  * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
                     33:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     34:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
                     35:  * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
                     36:  * THE POSSIBILITY OF SUCH DAMAGE.
                     37:  *
                     38:  * Author: Archie Cobbs <archie@freebsd.org>
                     39:  */
                     40: 
                     41: #include <sys/types.h>
                     42: #include <sys/stat.h>
                     43: 
                     44: #include <netinet/in_systm.h>
                     45: #include <netinet/in.h>
                     46: 
                     47: #include <stdlib.h>
                     48: #include <stdio.h>
                     49: #include <string.h>
                     50: #include <stdarg.h>
                     51: #include <syslog.h>
                     52: #include <errno.h>
                     53: #include <assert.h>
                     54: #include <pthread.h>
                     55: 
                     56: #include <openssl/ssl.h>
                     57: 
                     58: #include "structs/structs.h"
                     59: #include "structs/type/array.h"
                     60: 
                     61: #include "tmpl/tmpl.h"
                     62: #include "http/http_defs.h"
                     63: #include "http/http_server.h"
                     64: #include "http/http_servlet.h"
                     65: #include "http/servlet/tmpl.h"
                     66: #include "util/typed_mem.h"
                     67: 
                     68: #define MEM_TYPE       "http_servlet_tmpl"
                     69: 
                     70: /* Template servlet private context */
                     71: struct tmpl_private {
                     72:        struct http_servlet_tmpl_info   info;   /* static template info */
                     73:        struct tmpl                     *tmpl;  /* parsed template */
                     74:        struct timespec                 mtime;  /* last mod time of file */
                     75:        pthread_rwlock_t                lock;   /* servlet r/w lock */
                     76: };
                     77: 
                     78: /* Private info per servlet instantiation */
                     79: struct tmpl_instance {
                     80:        struct tmpl_private             *priv;  /* back pointer to servlet */
                     81:        struct tmpl_ctx                 *ctx;   /* tmpl exectution context */
                     82:        struct http_servlet_tmpl_arg    targ;   /* arg for handler funcs */
                     83: };
                     84: 
                     85: /* Internal functions */
                     86: static http_servlet_run_t      http_servlet_tmpl_run;
                     87: static http_servlet_destroy_t  http_servlet_tmpl_destroy;
                     88: 
                     89: static void    http_servlet_tmpl_run_cleanup(void *arg);
                     90: 
                     91: /*
                     92:  * Create a new template servlet.
                     93:  */
                     94: struct http_servlet *
                     95: http_servlet_tmpl_create(struct http_servlet_tmpl_info *info)
                     96: {
                     97:        struct http_servlet *servlet = NULL;
                     98:        struct tmpl_private *priv = NULL;
                     99: 
                    100:        /* Create servlet */
                    101:        if ((servlet = MALLOC(MEM_TYPE, sizeof(*servlet))) == NULL)
                    102:                goto fail;
                    103:        memset(servlet, 0, sizeof(*servlet));
                    104:        servlet->run = http_servlet_tmpl_run;
                    105:        servlet->destroy = http_servlet_tmpl_destroy;
                    106: 
                    107:        /* Initialize private info */
                    108:        if ((priv = MALLOC(MEM_TYPE, sizeof(*priv))) == NULL)
                    109:                goto fail;
                    110:        memset(priv, 0, sizeof(*priv));
                    111:        if (info->path != NULL
                    112:            && (priv->info.path = STRDUP(MEM_TYPE, info->path)) == NULL)
                    113:                goto fail;
                    114:        if (info->mime_type != NULL
                    115:            && (priv->info.mime_type = STRDUP(MEM_TYPE,
                    116:              info->mime_type)) == NULL)
                    117:                goto fail;
                    118:        if (info->mime_encoding != NULL
                    119:            && (priv->info.mime_encoding = STRDUP(MEM_TYPE,
                    120:              info->mime_encoding)) == NULL)
                    121:                goto fail;
                    122:        priv->info.logger = info->logger;
                    123:        if (_http_servlet_tmpl_copy_tinfo(&priv->info.tinfo,
                    124:            &info->tinfo) == -1)
                    125:                goto fail;
                    126:        pthread_rwlock_init(&priv->lock, NULL);
                    127:        servlet->arg = priv;
                    128: 
                    129:        /* OK */
                    130:        return (servlet);
                    131: 
                    132: fail:
                    133:        if (priv != NULL) {
                    134:                FREE(MEM_TYPE, (char *)priv->info.path);
                    135:                FREE(MEM_TYPE, (char *)priv->info.mime_type);
                    136:                FREE(MEM_TYPE, (char *)priv->info.mime_encoding);
                    137:                _http_servlet_tmpl_free_tinfo(&priv->info.tinfo);
                    138:                FREE(MEM_TYPE, priv);
                    139:        }
                    140:        if (servlet != NULL)
                    141:                FREE(MEM_TYPE, servlet);
                    142:        return (NULL);
                    143: }
                    144: 
                    145: /*
                    146:  * Destroy template servlet.
                    147:  */
                    148: static void
                    149: http_servlet_tmpl_destroy(struct http_servlet *servlet)
                    150: {
                    151:        struct tmpl_private *const priv = servlet->arg;
                    152:        struct http_servlet_tmpl_info *const info = &priv->info;
                    153:        int r;
                    154: 
                    155:        /* Acquire the lock to avoid race condition */
                    156:        r = pthread_rwlock_wrlock(&priv->lock);
                    157:        assert(r == 0);
                    158: 
                    159:        /* Destroy user argument */
                    160:        if (info->tinfo.freer != NULL)
                    161:                (*info->tinfo.freer)(info->tinfo.arg);
                    162: 
                    163:        /* Destroy template context */
                    164:        tmpl_destroy(&priv->tmpl);
                    165: 
                    166:        /* Release the lock */
                    167:        r = pthread_rwlock_unlock(&priv->lock);
                    168:        assert(r == 0);
                    169: 
                    170:        /* Free structure */
                    171:        FREE(MEM_TYPE, (char *)info->path);
                    172:        FREE(MEM_TYPE, (char *)info->mime_type);
                    173:        FREE(MEM_TYPE, (char *)info->mime_encoding);
                    174:        _http_servlet_tmpl_free_tinfo(&info->tinfo);
                    175:        FREE(MEM_TYPE, priv);
                    176:        FREE(MEM_TYPE, servlet);
                    177: }
                    178: 
                    179: /*
                    180:  * Run template servlet
                    181:  */
                    182: static int
                    183: http_servlet_tmpl_run(struct http_servlet *servlet,
                    184:        struct http_request *req, struct http_response *resp)
                    185: {
                    186:        struct tmpl_private *const priv = servlet->arg;
                    187:        struct http_servlet_tmpl_info *const info = &priv->info;
                    188:        struct http_servlet_tmpl_tinfo *const tinfo = &priv->info.tinfo;
                    189:        struct tmpl_instance *this = NULL;
                    190:        FILE *output = NULL;
                    191:        const char *hval;
                    192:        struct stat sb;
                    193:        int num_errors;
                    194:        int r;
                    195: 
                    196:        /* Construct per-instance state */
                    197:        if ((this = MALLOC(MEM_TYPE, sizeof(*this))) == NULL) {
                    198:                (*info->logger)(LOG_ERR, "%s: %s: %s",
                    199:                    __FUNCTION__, "malloc", strerror(errno));
                    200:                return (-1);
                    201:        }
                    202:        memset(this, 0, sizeof(*this));
                    203:        this->priv = priv;
                    204: 
                    205:        /* Grab lock to avoid race with http_servlet_tmpl_destroy() */
                    206:        r = pthread_rwlock_rdlock(&priv->lock);
                    207:        assert(r == 0);
                    208: 
                    209:        /* Push cleanup hook in case thread gets canceled */
                    210:        pthread_cleanup_push(http_servlet_tmpl_run_cleanup, this);
                    211: 
                    212:        /* Get servlet output stream (buffered) */
                    213:        if ((output = http_response_get_output(resp, 1)) == NULL) {
                    214:                (*info->logger)(LOG_ERR, "can't get template output: %s",
                    215:                    strerror(errno));
                    216:                goto fail_errno;
                    217:        }
                    218: 
                    219:        /* Set MIME type */
                    220:        if (info->mime_type == NULL) {
                    221:                http_response_set_header(resp, 0,
                    222:                    HTTP_HEADER_CONTENT_TYPE, "text/html; charset=iso-8859-1");
                    223:        } else {
                    224:                http_response_set_header(resp, 0,
                    225:                    HTTP_HEADER_CONTENT_TYPE, "%s", info->mime_type);
                    226:                if (info->mime_encoding != NULL) {
                    227:                        http_response_set_header(resp,
                    228:                        0, HTTP_HEADER_CONTENT_ENCODING,
                    229:                            "%s", info->mime_encoding);
                    230:                }
                    231:        }
                    232: 
                    233:        /* Assume servlet output is not cachable */
                    234:        http_response_set_header(resp, 1, HTTP_HEADER_PRAGMA, "no-cache");
                    235:        http_response_set_header(resp, 0,
                    236:            HTTP_HEADER_CACHE_CONTROL, "no-cache");
                    237: 
                    238:        /* Get modification timestamp of the template file */
                    239:        if (stat(info->path, &sb) == -1) {
                    240:                (*info->logger)(LOG_ERR, "%s: %s: %s",
                    241:                    __FUNCTION__, info->path, strerror(errno));
                    242:                memset(&sb.st_mtime, 0, sizeof(sb.st_mtime));
                    243:        }
                    244: 
                    245:        /* Invalidate cached template if template file has changed */
                    246:        if (priv->tmpl != NULL
                    247:            && memcmp(&sb.st_mtime, &priv->mtime, sizeof(priv->mtime)) != 0) {
                    248:                (*info->logger)(LOG_INFO,
                    249:                    "template \"%s\" was updated", info->path);
                    250:                tmpl_destroy(&priv->tmpl);
                    251:        }
                    252: 
                    253:        /* Do we need to (re)parse the template? */
                    254:        if (priv->tmpl == NULL) {
                    255: 
                    256:                /* Parse template file */
                    257:                if ((priv->tmpl = tmpl_create_mmap(info->path,
                    258:                    &num_errors, tinfo->mtype)) == NULL) {
                    259:                        (*info->logger)(LOG_ERR,
                    260:                            "can't create template from \"%s\": %s",
                    261:                            info->path, strerror(errno));
                    262:                        goto fail_errno;
                    263:                }
                    264: 
                    265:                /* Check for an error from tmpl_create() */
                    266:                if (priv->tmpl == NULL) {
                    267:                        (*info->logger)(LOG_ERR,
                    268:                            "can't create \"%s\" template: %s", info->path,
                    269:                            strerror(errno));
                    270:                        goto fail_errno;
                    271:                }
                    272: 
                    273:                /* Warn if there were any parse errors */
                    274:                if (num_errors != 0) {
                    275:                        (*info->logger)(LOG_WARNING,
                    276:                            "%d parse error%s in template \"%s\"",
                    277:                            num_errors, num_errors == 1 ? "" : "s",
                    278:                            info->path);
                    279:                }
                    280: 
                    281:                /* Update last modified time */
                    282:                memcpy(&priv->mtime, &sb.st_mtime, sizeof(priv->mtime));
                    283:        }
                    284: 
                    285:        /* Read URL-encoded form data if this is a normal POST */
                    286:        if (strcmp(http_request_get_method(req), HTTP_METHOD_POST) == 0
                    287:            && (hval = http_request_get_header(req,
                    288:              HTTP_HEADER_CONTENT_TYPE)) != NULL
                    289:            && strcasecmp(hval, HTTP_CTYPE_FORM_URLENCODED) == 0) {
                    290:                if (http_request_read_url_encoded_values(req) == -1) {
                    291:                        (*info->logger)(LOG_ERR,
                    292:                            "error reading %s data for \"%s\" template: %s",
                    293:                            HTTP_METHOD_POST, info->path, strerror(errno));
                    294:                        goto fail_errno;
                    295:                }
                    296:        }
                    297: 
                    298:        /* Fill in handler function cookie */
                    299:        this->targ.arg = info->tinfo.arg;
                    300:        this->targ.req = req;
                    301:        this->targ.resp = resp;
                    302: 
                    303:        /* Create tmpl execution context */
                    304:        if ((this->ctx = tmpl_ctx_create(&this->targ,
                    305:            tinfo->mtype, tinfo->handler, tinfo->errfmtr)) == NULL) {
                    306:                (*info->logger)(LOG_ERR, "%s: %s: %s",
                    307:                    __FUNCTION__, "tmpl_ctx_create", strerror(errno));
                    308:                goto fail_errno;
                    309:        }
                    310: 
                    311:        /* Execute template */
                    312:        if (tmpl_execute(priv->tmpl, this->ctx, output, tinfo->flags) == -1) {
                    313:                (*info->logger)(LOG_ERR, "can't execute \"%s\" template: %s",
                    314:                    info->path, strerror(errno));
                    315:                goto fail_errno;
                    316:        }
                    317: 
                    318:        /* OK */
                    319:        goto done;
                    320: 
                    321: fail_errno:
                    322:        /* Fail with appropriate error response */
                    323:        http_response_send_errno_error(resp);
                    324: 
                    325: done:
                    326:        /* Done */
                    327:        if (output != NULL)
                    328:                fclose(output);
                    329:        pthread_cleanup_pop(1);
                    330:        return (1);
                    331: }
                    332: 
                    333: /*
                    334:  * Cleanup after http_servlet_tmpl_run().
                    335:  */
                    336: static void
                    337: http_servlet_tmpl_run_cleanup(void *arg)
                    338: {
                    339:        struct tmpl_instance *const this = arg;
                    340:        int r;
                    341: 
                    342:        if (this->ctx != NULL)
                    343:                tmpl_ctx_destroy(&this->ctx);
                    344:        r = pthread_rwlock_unlock(&this->priv->lock);
                    345:        assert(r == 0);
                    346:        FREE(MEM_TYPE, this);
                    347: }
                    348: 
                    349: /*
                    350:  * Copy a 'tinfo' structure.
                    351:  */
                    352: int
                    353: _http_servlet_tmpl_copy_tinfo(struct http_servlet_tmpl_tinfo *dst,
                    354:        const struct http_servlet_tmpl_tinfo *src)
                    355: {
                    356: 
                    357:        /* Copy stuff */
                    358:        memset(dst, 0, sizeof(*dst));
                    359:        dst->flags = src->flags;
                    360:        dst->handler = src->handler;
                    361:        dst->errfmtr = src->errfmtr;
                    362:        if (src->mtype != NULL
                    363:            && (dst->mtype = STRDUP(MEM_TYPE, src->mtype)) == NULL)
                    364:                goto fail;
                    365:        dst->arg = src->arg;
                    366:        dst->freer = src->freer;
                    367: 
                    368:        /* Done */
                    369:        return (0);
                    370: 
                    371: fail:
                    372:        /* Clean up after failure */
                    373:        FREE(MEM_TYPE, (char *)dst->mtype);
                    374:        memset(dst, 0, sizeof(*dst));
                    375:        return (-1);
                    376: }
                    377: 
                    378: /*
                    379:  * Free a copied 'tinfo' structure.
                    380:  */
                    381: void
                    382: _http_servlet_tmpl_free_tinfo(struct http_servlet_tmpl_tinfo *tinfo)
                    383: {
                    384:        FREE(MEM_TYPE, (char *)tinfo->mtype);
                    385:        memset(tinfo, 0, sizeof(*tinfo));
                    386: }
                    387: 
                    388: /************************************************************************
                    389:                            TMPL USER FUNCTIONS
                    390: ************************************************************************/
                    391: 
                    392: char *
                    393: http_servlet_tmpl_func_query(struct tmpl_ctx *ctx,
                    394:        char **errmsgp, int ac, char **av)
                    395: {
                    396:        struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx);
                    397:        const char *const mtype = tmpl_ctx_get_mtype(ctx);
                    398:        const char *value;
                    399: 
                    400:        if (ac != 2) {
                    401:                errno = EINVAL;
                    402:                return (NULL);
                    403:        }
                    404:        if ((value = http_request_get_value(arg->req, av[1], 0)) == NULL)
                    405:                value = "";
                    406:        return (STRDUP(mtype, value));
                    407: }
                    408: 
                    409: char *
                    410: http_servlet_tmpl_func_query_exists(struct tmpl_ctx *ctx,
                    411:        char **errmsgp, int ac, char **av)
                    412: {
                    413:        struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx);
                    414:        const char *const mtype = tmpl_ctx_get_mtype(ctx);
                    415:        char buf[2];
                    416: 
                    417:        if (ac != 2) {
                    418:                errno = EINVAL;
                    419:                return (NULL);
                    420:        }
                    421:        snprintf(buf, sizeof(buf), "%d",
                    422:            http_request_get_value(arg->req, av[1], 0) != NULL);
                    423:        return (STRDUP(mtype, buf));
                    424: }
                    425: 
                    426: char *
                    427: http_servlet_tmpl_func_query_string(struct tmpl_ctx *ctx,
                    428:        char **errmsgp, int ac, char **av)
                    429: {
                    430:        struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx);
                    431:        const char *const mtype = tmpl_ctx_get_mtype(ctx);
                    432:        const char *eqs = http_request_get_query_string(arg->req);
                    433:        char *dqs;
                    434: 
                    435:        if (ac != 1) {
                    436:                errno = EINVAL;
                    437:                return (NULL);
                    438:        }
                    439:        /* URL-decode query string */
                    440:        if ((dqs = MALLOC(mtype, strlen(eqs) + 1)) == NULL)
                    441:                return (NULL);
                    442:        http_request_url_decode(eqs, dqs);
                    443: 
                    444:        /* Return it */
                    445:        return (dqs);
                    446: }
                    447: 
                    448: char *
                    449: http_servlet_tmpl_func_get_header(struct tmpl_ctx *ctx,
                    450:        char **errmsgp, int ac, char **av)
                    451: {
                    452:        struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx);
                    453:        const char *const mtype = tmpl_ctx_get_mtype(ctx);
                    454:        const char *hval;
                    455: 
                    456:        if (ac != 2) {
                    457:                errno = EINVAL;
                    458:                return (NULL);
                    459:        }
                    460:        if ((hval = http_request_get_header(arg->req, av[1])) == NULL)
                    461:                hval = "";
                    462:        return (STRDUP(mtype, hval));
                    463: }
                    464: 
                    465: char *
                    466: http_servlet_tmpl_func_set_header(struct tmpl_ctx *ctx,
                    467:        char **errmsgp, int ac, char **av)
                    468: {
                    469:        struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx);
                    470:        const char *const mtype = tmpl_ctx_get_mtype(ctx);
                    471: 
                    472:        if (ac != 3) {
                    473:                errno = EINVAL;
                    474:                return (NULL);
                    475:        }
                    476:        if (http_response_set_header(arg->resp, 0, av[1], "%s", av[2]) == -1)
                    477:                return (NULL);
                    478:        return (STRDUP(mtype, ""));
                    479: }
                    480: 
                    481: char *
                    482: http_servlet_tmpl_func_remove_header(struct tmpl_ctx *ctx,
                    483:        char **errmsgp, int ac, char **av)
                    484: {
                    485:        struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx);
                    486:        const char *const mtype = tmpl_ctx_get_mtype(ctx);
                    487: 
                    488:        if (ac != 2) {
                    489:                errno = EINVAL;
                    490:                return (NULL);
                    491:        }
                    492:        (void)http_response_remove_header(arg->resp, av[1]);
                    493:        return (STRDUP(mtype, ""));
                    494: }
                    495: 
                    496: char *
                    497: http_servlet_tmpl_func_unbuffer(struct tmpl_ctx *ctx,
                    498:        char **errmsgp, int ac, char **av)
                    499: {
                    500:        struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx);
                    501:        const char *const mtype = tmpl_ctx_get_mtype(ctx);
                    502: 
                    503:        if (ac != 1) {
                    504:                errno = EINVAL;
                    505:                return (NULL);
                    506:        }
                    507:        http_response_send_headers(arg->resp, 1);
                    508:        return (STRDUP(mtype, ""));
                    509: }
                    510: 
                    511: char *
                    512: http_servlet_tmpl_func_redirect(struct tmpl_ctx *ctx,
                    513:        char **errmsgp, int ac, char **av)
                    514: {
                    515:        struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx);
                    516:        const char *const mtype = tmpl_ctx_get_mtype(ctx);
                    517: 
                    518:        if (ac != 2) {
                    519:                errno = EINVAL;
                    520:                return (NULL);
                    521:        }
                    522:        if (http_response_set_header(arg->resp, 0,
                    523:            HTTP_HEADER_LOCATION, "%s", av[1]) == -1)
                    524:                return (NULL);
                    525:        http_response_send_error(arg->resp, HTTP_STATUS_FOUND, NULL);
                    526:        return (STRDUP(mtype, ""));
                    527: }
                    528: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>