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