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