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>