Annotation of embedaddon/libpdel/http/servlet/http_servlet_tmpl.c, revision 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>