ksgi
/
template.c
251 строка · 5.9 Кб
1/* $Id$ */
2/*
3* Copyright (c) 2017--2018 Kristaps Dzonsons <kristaps@bsd.lv>
4*
5* Permission to use, copy, modify, and distribute this software for any
6* purpose with or without fee is hereby granted, provided that the above
7* copyright notice and this permission notice appear in all copies.
8*
9* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*/
17#include "config.h"18
19#include <sys/mman.h>20#include <sys/stat.h>21
22#include <fcntl.h>23#include <limits.h>24#include <stdarg.h>25#include <stdint.h>26#include <stdlib.h>27#include <string.h>28#include <unistd.h>29
30#include "kcgi.h"31#include "extern.h"32
33static enum kcgi_err34khttp_templatex_write(const char *dat, size_t sz, void *arg)35{
36
37return khttp_write(arg, dat, sz);38}
39
40enum kcgi_err41khttp_template_buf(struct kreq *req,42const struct ktemplate *t, const char *buf, size_t sz)43{
44struct ktemplatex x;45
46memset(&x, 0, sizeof(struct ktemplatex));47x.writer = khttp_templatex_write;48
49return khttp_templatex_buf(t, buf, sz, &x, req);50}
51
52/*
53* There are all sorts of ways to make this faster and more efficient.
54* For now, do it the easily-auditable way.
55* Memory-map the given file and look through it character by character
56* til we get to the key delimiter "@@".
57* Once there, scan to the matching "@@".
58* Look for the matching key within these pairs.
59* If found, invoke the callback function with the given key.
60*/
61enum kcgi_err62khttp_templatex_buf(const struct ktemplate *t,63const char *buf, size_t sz,64const struct ktemplatex *opt, void *arg)65{
66size_t i, j, len, start, end;67ktemplate_writef fp;68enum kcgi_err er;69
70if (sz == 0)71return KCGI_OK;72
73fp = opt->writer;74
75/*76* If we have no callback mechanism, then we're going to push
77* the unmodified text out.
78* Check both the per-key template system and the fallback, if
79* provided.
80*/
81
82if (t == NULL && opt->fbk == NULL)83return fp(buf, sz, arg);84
85for (i = 0; i < sz - 1; i++) {86/*87* Read ahead til one of our significant characters.
88* Then emit all characters between then and now.
89*/
90
91for (j = i; j < sz - 1; j++)92if (buf[j] == '\\' || buf[j] == '@')93break;94if (j > i && (er = fp(&buf[i], j - i, arg)) != KCGI_OK)95return er;96i = j;97
98/*99* See if we're at an escaped @@, i.e., it's preceded by
100* the backslash.
101* If we are, then emit the standalone @@.
102*/
103
104if (i < sz - 2 && buf[i] == '\\' &&105buf[i + 1] == '@' && buf[i + 2] == '@') {106if ((er = fp(&buf[i + 1], 2, arg)) != KCGI_OK)107return er;108i += 2;109continue;110}111
112/* Look for the starting @@ marker. */113
114if (!(buf[i] == '@' && buf[i + 1] == '@')) {115if ((er = fp(&buf[i], 1, arg)) != KCGI_OK)116return er;117continue;118}119
120/* Seek to find the end "@@" marker. */121
122start = i + 2;123for (end = start; end < sz - 1; end++)124if (buf[end] == '@' && buf[end + 1] == '@')125break;126
127/* Continue printing if not found. */128
129if (end >= sz - 1) {130if ((er = fp(&buf[i], 1, arg)) != KCGI_OK)131return er;132continue;133}134
135/*136* Look for a matching key.
137* If we find no matching key, use the fallback (if
138* found), otherwise just continue as if the key were
139* opaque text.
140*/
141
142for (j = 0; j < t->keysz; j++) {143len = strlen(t->key[j]);144if (len != end - start)145continue;146else if (memcmp(&buf[start], t->key[j], len))147continue;148if (!(*t->cb)(j, t->arg)) {149kutil_warnx(NULL, NULL,150"template callback error");151return KCGI_FORM;152}153break;154}155
156if (j == t->keysz && opt->fbk != NULL) {157len = end - start;158if (!(*opt->fbk)(&buf[start], len, t->arg)) {159kutil_warnx(NULL, NULL, "template "160"default callback error");161return KCGI_FORM;162}163i = end + 1;164} else if (j == t->keysz) {165if ((er = fp(&buf[i], 1, arg)) != KCGI_OK)166return er;167} else168i = end + 1;169}170
171if (i < sz && (er = fp(&buf[i], 1, arg)) != KCGI_OK)172return er;173
174return KCGI_OK;175}
176
177enum kcgi_err178khttp_template(struct kreq *req,179const struct ktemplate *t, const char *fname)180{
181struct ktemplatex x;182
183memset(&x, 0, sizeof(struct ktemplatex));184x.writer = khttp_templatex_write;185return khttp_templatex(t, fname, &x, req);186}
187
188enum kcgi_err189khttp_templatex(const struct ktemplate *t,190const char *fname, const struct ktemplatex *opt, void *arg)191{
192int fd;193enum kcgi_err rc;194
195if ((fd = open(fname, O_RDONLY, 0)) == -1) {196kutil_warn(NULL, NULL, "%s", fname);197return KCGI_SYSTEM;198}199
200rc = khttp_templatex_fd(t, fd, fname, opt, arg);201close(fd);202return rc;203}
204
205enum kcgi_err206khttp_template_fd(struct kreq *req,207const struct ktemplate *t, int fd, const char *fname)208{
209struct ktemplatex x;210
211memset(&x, 0, sizeof(struct ktemplatex));212x.writer = khttp_templatex_write;213return khttp_templatex_fd(t, fd, fname, &x, req);214}
215
216enum kcgi_err217khttp_templatex_fd(const struct ktemplate *t,218int fd, const char *fname,219const struct ktemplatex *opt, void *arg)220{
221struct stat st;222char *buf;223size_t sz;224enum kcgi_err rc;225
226if (fname == NULL)227fname = "<unknown descriptor>";228
229if (fstat(fd, &st) == -1) {230kutil_warn(NULL, NULL, "%s", fname);231return KCGI_SYSTEM;232} else if (st.st_size > SSIZE_MAX) {233kutil_warnx(NULL, NULL, "%s: too large", fname);234return KCGI_SYSTEM;235} else if (st.st_size <= 0) {236kutil_warnx(NULL, NULL, "%s: zero-length", fname);237return KCGI_OK;238}239
240sz = (size_t)st.st_size;241buf = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);242
243if (buf == MAP_FAILED) {244kutil_warn(NULL, NULL, "%s", fname);245return KCGI_SYSTEM;246}247
248rc = khttp_templatex_buf(t, buf, sz, opt, arg);249munmap(buf, sz);250return rc;251}
252