Annotation of embedaddon/libpdel/http/servlet/http_servlet_file.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/param.h>
        !            43: #include <sys/socket.h>
        !            44: #include <sys/stat.h>
        !            45: #include <sys/uio.h>
        !            46: #ifdef __linux__
        !            47: #include <sys/sendfile.h>
        !            48: #endif
        !            49: 
        !            50: #include <netinet/in.h>
        !            51: 
        !            52: #include <stdlib.h>
        !            53: #include <stdio.h>
        !            54: #include <string.h>
        !            55: #include <stdarg.h>
        !            56: #include <syslog.h>
        !            57: #include <fcntl.h>
        !            58: #include <unistd.h>
        !            59: #include <errno.h>
        !            60: #include <assert.h>
        !            61: #include <pthread.h>
        !            62: 
        !            63: #include <openssl/ssl.h>
        !            64: 
        !            65: #include "structs/structs.h"
        !            66: #include "structs/type/array.h"
        !            67: 
        !            68: #include "sys/alog.h"
        !            69: #include "io/string_fp.h"
        !            70: #include "tmpl/tmpl.h"
        !            71: #include "util/ghash.h"
        !            72: #include "util/typed_mem.h"
        !            73: 
        !            74: #include "http/http_defs.h"
        !            75: #include "http/http_server.h"
        !            76: #include "http/http_servlet.h"
        !            77: #include "http/servlet/tmpl.h"
        !            78: #include "http/servlet/file.h"
        !            79: 
        !            80: #define        MEM_TYPE        "http_servlet_file"
        !            81: #define        TMPL_SUFFIX     ".tmpl"
        !            82: 
        !            83: /* Cleanup state for http_servlet_file_serve() */
        !            84: struct http_servlet_file_serve_state {
        !            85:        int     fd;
        !            86: };
        !            87: 
        !            88: /* One cached template */
        !            89: struct tmpl_cache {
        !            90:        char                    *path;          /* file pathname */
        !            91:        struct http_servlet     *servlet;       /* tmpl servlet */
        !            92: };
        !            93: 
        !            94: /* Servlet state */
        !            95: struct file_private {
        !            96:        struct http_servlet_file_info   *info;
        !            97:        struct ghash                    *tmpls; /* cached tmpl servlets */
        !            98:        http_servlet_tmpl_free_t        *freer; /* freer for tmpl arg */
        !            99: };
        !           100: 
        !           101: /* Directory -> file redirects */
        !           102: static const   char *http_servlet_file_dirindex[][2] = {
        !           103:        { "index.tmpl", "index" },
        !           104:        { "index.html", "index.html" },
        !           105:        { "index.htm",  "index.htm" },
        !           106:        { NULL, NULL }
        !           107: };
        !           108: 
        !           109: /*
        !           110:  * Internal functions
        !           111:  */
        !           112: static char    *http_servlet_file_gen_filename(
        !           113:                        struct http_servlet_file_info *finfo, const char *url,
        !           114:                        const char *mtype);
        !           115: static void    http_servlet_file_tmpl(struct http_servlet *servlet,
        !           116:                        const char *path, struct http_request *req,
        !           117:                        struct http_response *resp);
        !           118: static void    http_servlet_file_serve_cleanup(void *arg);
        !           119: 
        !           120: static http_servlet_run_t      http_servlet_file_run;
        !           121: static http_servlet_destroy_t  http_servlet_file_destroy;
        !           122: 
        !           123: static ghash_hash_t    tmpl_cache_hash;
        !           124: static ghash_equal_t   tmpl_cache_equal;
        !           125: static ghash_del_t     tmpl_cache_del;
        !           126: 
        !           127: /*
        !           128:  * Create a new file servlet.
        !           129:  */
        !           130: struct http_servlet *
        !           131: http_servlet_file_create(const struct http_servlet_file_info *info)
        !           132: {
        !           133:        struct http_servlet *servlet = NULL;
        !           134:        struct file_private *priv = NULL;
        !           135: 
        !           136:        /* Create servlet */
        !           137:        if ((servlet = MALLOC(MEM_TYPE, sizeof(*servlet))) == NULL)
        !           138:                goto fail;
        !           139:        memset(servlet, 0, sizeof(*servlet));
        !           140:        servlet->run = http_servlet_file_run;
        !           141:        servlet->destroy = http_servlet_file_destroy;
        !           142: 
        !           143:        /* Initialize private info */
        !           144:        if ((priv = MALLOC(MEM_TYPE, sizeof(*priv))) == NULL)
        !           145:                goto fail;
        !           146:        memset(priv, 0, sizeof(*priv));
        !           147: 
        !           148:        /* Copy "info" */
        !           149:        if ((priv->info = MALLOC(MEM_TYPE, sizeof(*priv->info))) == NULL)
        !           150:                goto fail;
        !           151:        memset(priv->info, 0, sizeof(*priv->info));
        !           152:        if (info->docroot != NULL
        !           153:            && (priv->info->docroot = STRDUP(MEM_TYPE, info->docroot)) == NULL)
        !           154:                goto fail;
        !           155:        priv->info->allow_escape = info->allow_escape;
        !           156:        if (info->filename != NULL
        !           157:            && (priv->info->filename
        !           158:              = STRDUP(MEM_TYPE, info->filename)) == NULL)
        !           159:                goto fail;
        !           160:        if (info->prefix != NULL
        !           161:            && (priv->info->prefix = STRDUP(MEM_TYPE, info->prefix)) == NULL)
        !           162:                goto fail;
        !           163:        if (info->mime_type != NULL
        !           164:            && (priv->info->mime_type
        !           165:              = STRDUP(MEM_TYPE, info->mime_type)) == NULL)
        !           166:                goto fail;
        !           167:        if (info->mime_encoding != NULL
        !           168:            && (priv->info->mime_encoding
        !           169:              = STRDUP(MEM_TYPE, info->mime_encoding)) == NULL)
        !           170:                goto fail;
        !           171:        priv->info->logger = info->logger;
        !           172:        priv->info->hide = info->hide;
        !           173:        if (_http_servlet_tmpl_copy_tinfo(&priv->info->tinfo,
        !           174:            &info->tinfo) == -1)
        !           175:                goto fail;
        !           176: 
        !           177:        /* Only free the template argument once */
        !           178:        priv->freer = priv->info->tinfo.freer;
        !           179:        priv->info->tinfo.freer = NULL;
        !           180: 
        !           181:        /* Create template hash table */
        !           182:        if ((priv->tmpls = ghash_create(priv, 0, 200, MEM_TYPE,
        !           183:            tmpl_cache_hash, tmpl_cache_equal, NULL, tmpl_cache_del)) == NULL)
        !           184:                goto fail;
        !           185:        servlet->arg = priv;
        !           186: 
        !           187:        /* OK */
        !           188:        return (servlet);
        !           189: 
        !           190: fail:
        !           191:        /* Clean up after failure */
        !           192:        if (priv != NULL) {
        !           193:                if (priv->info != NULL) {
        !           194:                        FREE(MEM_TYPE, (char *)priv->info->filename);
        !           195:                        FREE(MEM_TYPE, (char *)priv->info->docroot);
        !           196:                        FREE(MEM_TYPE, (char *)priv->info->prefix);
        !           197:                        FREE(MEM_TYPE, (char *)priv->info->mime_type);
        !           198:                        FREE(MEM_TYPE, (char *)priv->info->mime_encoding);
        !           199:                        _http_servlet_tmpl_free_tinfo(&priv->info->tinfo);
        !           200:                        FREE(MEM_TYPE, priv->info);
        !           201:                }
        !           202:                ghash_destroy(&priv->tmpls);
        !           203:                FREE(MEM_TYPE, priv);
        !           204:        }
        !           205:        if (servlet != NULL)
        !           206:                FREE(MEM_TYPE, servlet);
        !           207:        return (NULL);
        !           208: }
        !           209: 
        !           210: /*
        !           211:  * Destroy a file servlet.
        !           212:  */
        !           213: static void
        !           214: http_servlet_file_destroy(struct http_servlet *servlet)
        !           215: {
        !           216:        struct file_private *const priv = servlet->arg;
        !           217: 
        !           218:        /* Free template argument */
        !           219:        if (priv->freer != NULL)
        !           220:                (*priv->freer)(priv->info->tinfo.arg);
        !           221: 
        !           222:        /* Free private info */
        !           223:        FREE(MEM_TYPE, (char *)priv->info->filename);
        !           224:        FREE(MEM_TYPE, (char *)priv->info->docroot);
        !           225:        FREE(MEM_TYPE, (char *)priv->info->prefix);
        !           226:        FREE(MEM_TYPE, (char *)priv->info->mime_type);
        !           227:        FREE(MEM_TYPE, (char *)priv->info->mime_encoding);
        !           228:        _http_servlet_tmpl_free_tinfo(&priv->info->tinfo);
        !           229:        FREE(MEM_TYPE, priv->info);
        !           230:        ghash_destroy(&priv->tmpls);
        !           231:        FREE(MEM_TYPE, priv);
        !           232: 
        !           233:        /* Free servlet */
        !           234:        FREE(MEM_TYPE, servlet);
        !           235: }
        !           236: 
        !           237: /*
        !           238:  * Execute file servlet.
        !           239:  */
        !           240: static int
        !           241: http_servlet_file_run(struct http_servlet *servlet,
        !           242:        struct http_request *req, struct http_response *resp)
        !           243: {
        !           244:        struct file_private *const priv = servlet->arg;
        !           245:        struct http_servlet_file_info *const info = priv->info;
        !           246:        const char *const urlpath = http_request_get_path(req);
        !           247:        char *path = NULL;
        !           248:        int got_tmpl = 0;
        !           249:        size_t len;
        !           250: 
        !           251:        /* Generate file name from URL; first try to find a template file */
        !           252:        if (info->tinfo.handler != NULL
        !           253:            && (len = strlen(urlpath)) < MAXPATHLEN - sizeof(TMPL_SUFFIX)) {
        !           254:                char tp[MAXPATHLEN];
        !           255:                struct stat sb;
        !           256: 
        !           257:                memcpy(tp, urlpath, len);
        !           258:                memcpy(tp + len, TMPL_SUFFIX, sizeof(TMPL_SUFFIX));
        !           259:                path = http_servlet_file_gen_filename(info, tp, TYPED_MEM_TEMP);
        !           260:                if (stat(path, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFREG)
        !           261:                        got_tmpl = 1;
        !           262:                else {
        !           263:                        FREE(TYPED_MEM_TEMP, path);
        !           264:                        path = NULL;
        !           265:                }
        !           266:        }
        !           267: 
        !           268:        /* Generate file name from URL; now try a normal file */
        !           269:        if (!got_tmpl
        !           270:            && (path = http_servlet_file_gen_filename(info,
        !           271:              urlpath, TYPED_MEM_TEMP)) == NULL) {
        !           272:                http_response_send_errno_error(resp);
        !           273:                return (1);
        !           274:        }
        !           275: 
        !           276:        /* Handle templates */
        !           277:        if (got_tmpl) {
        !           278:                http_servlet_file_tmpl(servlet, path, req, resp);
        !           279:                goto done;
        !           280:        }
        !           281: 
        !           282:        /* Check whether to hide this file */
        !           283:        if (info->hide != NULL && (*info->hide)(info, req, resp, path)) {
        !           284:                FREE(TYPED_MEM_TEMP, path);
        !           285:                return (0);                     /* continue with next servlet */
        !           286:        }
        !           287: 
        !           288:        /* Use supplied MIME info, if any */
        !           289:        if (info->mime_type != NULL) {
        !           290:                http_response_set_header(resp, 0,
        !           291:                    HTTP_HEADER_CONTENT_TYPE, "%s", info->mime_type);
        !           292:                if (info->mime_encoding != NULL) {
        !           293:                        http_response_set_header(resp, 0,
        !           294:                            HTTP_HEADER_CONTENT_ENCODING,
        !           295:                            "%s", info->mime_encoding);
        !           296:                }
        !           297:        }
        !           298: 
        !           299:        /* Serve up file */
        !           300:        http_servlet_file_serve(path, info->logger, req, resp);
        !           301: 
        !           302: done:
        !           303:        /* Done */
        !           304:        FREE(TYPED_MEM_TEMP, path);
        !           305:        return (1);
        !           306: }
        !           307: 
        !           308: #define MAX_ENCODINGS  10
        !           309: 
        !           310: /*
        !           311:  * Serve up a file.
        !           312:  *
        !           313:  * This is a public function usable by other servlets.
        !           314:  */
        !           315: void
        !           316: http_servlet_file_serve(const char *path, http_logger_t *logger,
        !           317:        struct http_request *req, struct http_response *resp)
        !           318: {
        !           319:        const char *hval;
        !           320:        struct stat sb;
        !           321:        char buf[1024];
        !           322:        FILE *output;
        !           323:        struct tm tm;
        !           324:        time_t when;
        !           325:        int sock;
        !           326: 
        !           327:        /* Stat file */
        !           328:        if (stat(path, &sb) == -1) {
        !           329: fail_errno:    http_response_send_errno_error(resp);
        !           330:                return;
        !           331:        }
        !           332: 
        !           333:        /* If file is a directory, redirect to default file if it exists */
        !           334:        if ((sb.st_mode & S_IFMT) == S_IFDIR) {
        !           335:                int i;
        !           336: 
        !           337:                for (i = 0; http_servlet_file_dirindex[i][0] != NULL; i++) {
        !           338:                        const char *qs = http_request_get_query_string(req);
        !           339:                        char *urlpath;
        !           340:                        char *dfile;
        !           341: 
        !           342:                        if (qs == NULL)
        !           343:                                qs = "";
        !           344:                        ASPRINTF(TYPED_MEM_TEMP, &dfile, "%s/%s", path,
        !           345:                            http_servlet_file_dirindex[i][0]);
        !           346:                        if (dfile == NULL)
        !           347:                                goto fail_errno;
        !           348:                        if (stat(dfile, &sb) == -1) {
        !           349:                                FREE(TYPED_MEM_TEMP, dfile);
        !           350:                                continue;
        !           351:                        }
        !           352:                        if ((urlpath = http_request_url_encode(TYPED_MEM_TEMP,
        !           353:                            http_request_get_path(req))) == NULL) {
        !           354:                                FREE(TYPED_MEM_TEMP, dfile);
        !           355:                                goto fail_errno;
        !           356:                        }
        !           357:                        if (http_response_set_header(resp, 0,
        !           358:                            HTTP_HEADER_LOCATION, "%s%s%s%s%s", urlpath,
        !           359:                            "/" + (urlpath[strlen(urlpath) - 1] == '/'),
        !           360:                            http_servlet_file_dirindex[i][1],
        !           361:                            (*qs != '\0') ? "?" : "", qs) == -1) {
        !           362:                                FREE(TYPED_MEM_TEMP, urlpath);
        !           363:                                FREE(TYPED_MEM_TEMP, dfile);
        !           364:                                goto fail_errno;
        !           365:                        }
        !           366:                        FREE(TYPED_MEM_TEMP, urlpath);
        !           367:                        FREE(TYPED_MEM_TEMP, dfile);
        !           368:                        http_response_send_error(resp,
        !           369:                            HTTP_STATUS_FOUND, NULL);
        !           370:                        return;
        !           371:                }
        !           372:        }
        !           373: 
        !           374:        /* File must be regular */
        !           375:        if ((sb.st_mode & S_IFMT) != S_IFREG) {
        !           376:                errno = ENOENT;                 /* hide non-regular files */
        !           377:                goto fail_errno;
        !           378:        }
        !           379: 
        !           380:        /* Set timestamp from stat(2) info */
        !           381:        strftime(buf, sizeof(buf), HTTP_TIME_FMT_RFC1123,
        !           382:            gmtime_r(&sb.st_mtime, &tm));
        !           383:        http_response_set_header(resp, 0, HTTP_HEADER_DATE, "%s", buf);
        !           384: 
        !           385:        /* Check for If-Modified-Since: header */
        !           386:        if ((hval = http_request_get_header(req,
        !           387:            HTTP_HEADER_IF_MODIFIED_SINCE)) != NULL) {
        !           388:                if ((when = http_request_parse_time(hval)) != (time_t)-1
        !           389:                    && sb.st_mtime <= when) {
        !           390:                        http_response_send_error(resp,
        !           391:                            HTTP_STATUS_NOT_MODIFIED, NULL);
        !           392:                        return;
        !           393:                }
        !           394:        }
        !           395: 
        !           396:        /* Set MIME type if not set already */
        !           397:        if (http_response_get_header(resp, HTTP_HEADER_CONTENT_TYPE) == NULL) {
        !           398:                const char *cencs[MAX_ENCODINGS];
        !           399:                const char *ctype;
        !           400:                int i;
        !           401: 
        !           402:                http_response_guess_mime(path, &ctype, cencs, MAX_ENCODINGS);
        !           403:                http_response_set_header(resp, 0,
        !           404:                    HTTP_HEADER_CONTENT_TYPE, "%s", ctype);
        !           405:                for (i = 0; i < MAX_ENCODINGS && cencs[i] != NULL; i++) {
        !           406:                        http_response_set_header(resp, i > 0,
        !           407:                            HTTP_HEADER_CONTENT_ENCODING, "%s", cencs[i]);
        !           408:                }
        !           409:        }
        !           410: 
        !           411:        /* Set content length */
        !           412:        http_response_set_header(resp, 0,
        !           413:            HTTP_HEADER_CONTENT_LENGTH, "%lu", (u_long)sb.st_size);
        !           414: 
        !           415:        /* Get servlet output stream (unbuffered) */
        !           416:        if ((output = http_response_get_output(resp, 0)) == NULL) {
        !           417:                (*logger)(LOG_ERR, "can't get response output: %s",
        !           418:                    strerror(errno));
        !           419:                return;
        !           420:        }
        !           421: 
        !           422:        /* Send file contents, using sendfile(2) if possible */
        !           423:        if ((sock = http_response_get_raw_socket(resp)) != -1) {
        !           424:                struct http_servlet_file_serve_state state;
        !           425: 
        !           426:                /* Open file */
        !           427:                if ((state.fd = open(path, O_RDONLY)) == -1)
        !           428:                        goto fail_errno;
        !           429: 
        !           430:                /* Set cleanup hook in case thread is canceled */
        !           431:                pthread_cleanup_push(http_servlet_file_serve_cleanup, &state);
        !           432: 
        !           433:                /* Make sure headers are sent first */
        !           434:                http_response_send_headers(resp, 1);
        !           435:                fflush(output);
        !           436: 
        !           437:                /* Send file directly using sendfile(2) */
        !           438: #ifndef __linux__
        !           439:                sendfile(state.fd, sock, 0, sb.st_size, NULL, NULL, 0);
        !           440: #else
        !           441:                sendfile(sock, state.fd, NULL, sb.st_size);
        !           442: #endif
        !           443: 
        !           444:                /* Close file */
        !           445:                pthread_cleanup_pop(1);
        !           446:        } else {
        !           447:                FILE *fp;
        !           448:                int ret;
        !           449: 
        !           450:                /* Open file */
        !           451:                if ((fp = fopen(path, "r")) == NULL)
        !           452:                        goto fail_errno;
        !           453: 
        !           454:                /* Set cleanup hook in case thread is canceled */
        !           455:                pthread_cleanup_push((void (*)(void *))fclose, fp);
        !           456: 
        !           457:                /* Tranfer file contents */
        !           458:                while (1) {
        !           459:                        if ((ret = fread(buf, 1, sizeof(buf), fp)) != 0) {
        !           460:                                if (fwrite(buf, 1, ret, output) < ret)
        !           461:                                        break;
        !           462:                        }
        !           463:                        if (ret < sizeof(buf))
        !           464:                                break;
        !           465:                }
        !           466: 
        !           467:                /* Close file */
        !           468:                pthread_cleanup_pop(1);
        !           469:        }
        !           470: }
        !           471: 
        !           472: /*
        !           473:  * Do a template file.
        !           474:  */
        !           475: static void
        !           476: http_servlet_file_tmpl(struct http_servlet *servlet, const char *path,
        !           477:        struct http_request *req, struct http_response *resp)
        !           478: {
        !           479:        struct file_private *const priv = servlet->arg;
        !           480:        struct http_servlet_file_info *const info = priv->info;
        !           481:        struct http_servlet_tmpl_info ti;
        !           482:        char mimepath[MAXPATHLEN + 1];
        !           483:        struct tmpl_cache *t = NULL;
        !           484:        struct tmpl_cache key;
        !           485:        const char *s;
        !           486: 
        !           487:        /* See if template already cached */
        !           488:        key.path = (char *)path;
        !           489:        if ((t = ghash_get(priv->tmpls, &key)) != NULL)
        !           490:                goto found;
        !           491: 
        !           492:        /* Create new cached entry */
        !           493:        if ((t = MALLOC(MEM_TYPE, sizeof(*t))) == NULL)
        !           494:                goto fail;
        !           495:        memset(t, 0, sizeof(*t));
        !           496:        if ((t->path = STRDUP(MEM_TYPE, path)) == NULL)
        !           497:                goto fail;
        !           498: 
        !           499:        /* Set info required by the template servlet */
        !           500:        memset(&ti, 0, sizeof(ti));
        !           501:        ti.path = t->path;
        !           502:        ti.tinfo = info->tinfo;
        !           503:        ti.logger = info->logger;
        !           504: 
        !           505:        /* Figure out templates's output MIME type */
        !           506:        strlcat(mimepath, path, sizeof(mimepath));
        !           507:        mimepath[strlen(mimepath) - strlen(TMPL_SUFFIX)] = '\0';
        !           508:        if ((s = strrchr(mimepath, '.')) == NULL
        !           509:            || strchr(s, '/') != NULL)          /* no suffix? assume html */
        !           510:                strlcat(mimepath, "x.html", sizeof(mimepath));
        !           511:        http_response_guess_mime(mimepath, &ti.mime_type, NULL, 0);
        !           512: 
        !           513:        /* Create template servlet */
        !           514:        if ((t->servlet = http_servlet_tmpl_create(&ti)) == NULL)
        !           515:                goto fail;
        !           516: 
        !           517:        /* Add it to hash table */
        !           518:        if (ghash_put(priv->tmpls, t) == -1) {
        !           519:                (*info->logger)(LOG_ERR,
        !           520:                    "%s: %s", "ghash_put", strerror(errno));
        !           521: fail:          FREE(MEM_TYPE, t);
        !           522:                http_response_send_errno_error(resp);
        !           523:                return;
        !           524:        }
        !           525: 
        !           526: found:
        !           527:        /* Invoke servlet */
        !           528:        (*t->servlet->run)(t->servlet, req, resp);
        !           529: }
        !           530: 
        !           531: static void
        !           532: http_servlet_file_serve_cleanup(void *arg)
        !           533: {
        !           534:        const struct http_servlet_file_serve_state *const state = arg;
        !           535: 
        !           536:        close(state->fd);
        !           537: }
        !           538: 
        !           539: static u_int32_t
        !           540: tmpl_cache_hash(struct ghash *g, const void *item)
        !           541: {
        !           542:        const struct tmpl_cache *const t = item;
        !           543:        u_int32_t hash;
        !           544:        const char *s;
        !           545: 
        !           546:        for (hash = 0, s = t->path; *s != '\0'; s++)
        !           547:                hash = (31 * hash) + (u_char)*s;
        !           548:        return (hash);
        !           549: }
        !           550: 
        !           551: static int
        !           552: tmpl_cache_equal(struct ghash *g, const void *item1, const void *item2)
        !           553: {
        !           554:        const struct tmpl_cache *const t1 = item1;
        !           555:        const struct tmpl_cache *const t2 = item2;
        !           556: 
        !           557:        return (strcmp(t1->path, t2->path) == 0);
        !           558: }
        !           559: 
        !           560: static void
        !           561: tmpl_cache_del(struct ghash *g, void *item)
        !           562: {
        !           563:        struct tmpl_cache *const t = item;
        !           564: 
        !           565:        http_server_destroy_servlet(&t->servlet);
        !           566:        FREE(MEM_TYPE, t->path);
        !           567:        FREE(MEM_TYPE, t);
        !           568: }
        !           569: 
        !           570: /*
        !           571:  * Compute a filename from supplied info and URL.
        !           572:  *
        !           573:  * Caller must free returned string, which is in a buffer of size MAXPATHLEN.
        !           574:  */
        !           575: static char *
        !           576: http_servlet_file_gen_filename(struct http_servlet_file_info *info,
        !           577:        const char *urlpath, const char *mtype)
        !           578: {
        !           579:        char path[MAXPATHLEN];
        !           580:        char *rpath;
        !           581:        char *tok;
        !           582:        char *s;
        !           583: 
        !           584:        /* Sanity check */
        !           585:        assert(*urlpath == '/');
        !           586: 
        !           587:        /* Disallow all ".", "..", and empty components within urlpath */
        !           588:        strlcpy(path, urlpath, sizeof(path));
        !           589:        for (s = path + 1; (tok = strsep(&s, "/")) != NULL; ) {
        !           590:                if ((*tok == '\0' && s != NULL)
        !           591:                     || strcmp(tok, ".") == 0 || strcmp(tok, "..") == 0) {
        !           592:                        errno = ENOENT;
        !           593:                        return (NULL);
        !           594:                }
        !           595:        }
        !           596: 
        !           597:        /* Prepend root directory, if any */
        !           598:        if (info->docroot != NULL) {
        !           599:                strlcpy(path, info->docroot, sizeof(path) - 1);
        !           600:                if (path[strlen(path) - 1] != '/')
        !           601:                        strlcat(path, "/", sizeof(path));
        !           602:        } else
        !           603:                *path = '\0';
        !           604: 
        !           605:        /* Add fixed filename, if any */
        !           606:        if (info->filename != NULL) {
        !           607:                if (*info->filename == '/')
        !           608:                        *path = '\0';
        !           609:                strlcat(path, info->filename, sizeof(path));
        !           610:                goto normalize;
        !           611:        }
        !           612: 
        !           613:        /* Strip URL prefix, if it matches */
        !           614:        if (info->prefix != NULL
        !           615:            && strncmp(urlpath, info->prefix, strlen(info->prefix)) == 0)
        !           616:                urlpath += strlen(info->prefix);
        !           617: 
        !           618:        /* Derive remainder of pathname from URL */
        !           619:        strlcat(path, urlpath + (*urlpath == '/'), sizeof(path));
        !           620: 
        !           621: normalize:
        !           622: 
        !           623:        /* Normalize path */
        !           624:        if ((rpath = MALLOC(mtype, MAXPATHLEN)) == NULL)
        !           625:                return (NULL);
        !           626:        if (realpath(path, rpath) == NULL) {
        !           627:                FREE(mtype, rpath);
        !           628:                return (NULL);
        !           629:        }
        !           630:        rpath[MAXPATHLEN - 1] = '\0';
        !           631: 
        !           632:        /* Verify that file is within the document root directory hierarchy */
        !           633:        if (!info->allow_escape) {
        !           634:                const char *docroot;
        !           635:                char *dpath;
        !           636:                size_t rlen;
        !           637:                int within;
        !           638: 
        !           639:                /* Use current working directory if info->docroot is NULL */
        !           640:                if (info->docroot == NULL) {
        !           641:                        getcwd(path, sizeof(path));
        !           642:                        path[sizeof(path) - 1] = '\0';
        !           643:                        docroot = path;
        !           644:                } else
        !           645:                        docroot = info->docroot;
        !           646: 
        !           647:                /* Normalize docroot path */
        !           648:                if ((dpath = MALLOC(mtype, MAXPATHLEN)) == NULL) {
        !           649:                        FREE(mtype, rpath);
        !           650:                        return (NULL);
        !           651:                }
        !           652:                if (realpath(docroot, dpath) == NULL) {
        !           653:                        FREE(mtype, dpath);
        !           654:                        FREE(mtype, rpath);
        !           655:                        return (NULL);
        !           656:                }
        !           657:                dpath[MAXPATHLEN - 1] = '\0';
        !           658: 
        !           659:                /* Verify that path is within the root */
        !           660:                rlen = strlen(dpath);
        !           661:                within = strncmp(rpath, dpath, rlen) == 0
        !           662:                    && (rpath[rlen] == '\0' || rpath[rlen] == '/');
        !           663:                FREE(mtype, dpath);
        !           664:                if (!within) {
        !           665:                        FREE(mtype, rpath);
        !           666:                        errno = ENOENT;
        !           667:                        return (NULL);
        !           668:                }
        !           669:        }
        !           670: 
        !           671:        /* Done */
        !           672:        return (rpath);
        !           673: }
        !           674: 

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