Annotation of embedaddon/libpdel/http/servlet/http_servlet_file.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/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>