diff --git a/src/template.c b/src/template.c index 4b952f3..1d4300f 100755 --- a/src/template.c +++ b/src/template.c @@ -1,677 +1,677 @@ -/* - * Copyright (C) 2017 Jianhui Zhao - * - * based on https://git.lede-project.org/project/luci.git modules/luci-base/src/template_parser.c - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "internal.h" -#include "uhttp/uhttp.h" - -/* code types */ -#define T_TYPE_INIT 0 -#define T_TYPE_TEXT 1 -#define T_TYPE_COMMENT 2 -#define T_TYPE_EXPR 3 -#define T_TYPE_INCLUDE 4 -#define T_TYPE_CODE 5 -#define T_TYPE_EOF 6 - -/* leading and trailing code for different types */ -static const char *gen_code[9][2] = { - { NULL, NULL }, /* T_TYPE_INIT */ - { "io.write(\"", "\")" }, /* T_TYPE_TEXT */ - { NULL, NULL }, /* T_TYPE_COMMENT */ - { "io.write(tostring(", " or \"\"))" }, /* T_TYPE_EXPR */ - { "include(\"", "\")" }, /* T_TYPE_INCLUDE */ - { NULL, " " }, /* T_TYPE_CODE */ - { NULL, NULL }, /* T_TYPE_EOF */ -}; - -/* buffer object */ -struct template_buffer { - char *data; - char *dptr; - unsigned int size; - unsigned int fill; -}; - -struct template_chunk { - const char *s; - const char *e; - int type; - int line; -}; - -/* parser state */ -struct template_parser { - int fd; - uint32_t size; - char *data; - char *off; - char *gc; - int line; - int in_expr; - int strip_before; - int strip_after; - struct template_chunk prv_chunk; - struct template_chunk cur_chunk; - const char *file; -}; - -/* initialize a buffer object */ -struct template_buffer *buf_init(int size) -{ - struct template_buffer *buf; - - if (size <= 0) - size = 1024; - - buf = (struct template_buffer *)malloc(sizeof(struct template_buffer)); - - if (buf != NULL) - { - buf->fill = 0; - buf->size = size; - buf->data = malloc(buf->size); - - if (buf->data != NULL) { - buf->dptr = buf->data; - buf->data[0] = 0; - return buf; - } - free(buf); - } - - return NULL; -} - -/* grow buffer */ -static int buf_grow(struct template_buffer *buf, int size) -{ - unsigned int off = (buf->dptr - buf->data); - char *data; - - if (size <= 0) - size = 1024; - - data = realloc(buf->data, buf->size + size); - - if (data != NULL) { - buf->data = data; - buf->dptr = data + off; - buf->size += size; - return buf->size; - } - - return 0; -} - -/* put one char into buffer object */ -static int buf_putchar(struct template_buffer *buf, char c) -{ - if( ((buf->fill + 1) >= buf->size) && !buf_grow(buf, 0) ) - return 0; - - *(buf->dptr++) = c; - *(buf->dptr) = 0; - - buf->fill++; - return 1; -} - -/* append data to buffer */ -static int buf_append(struct template_buffer *buf, const char *s, int len) -{ - if ((buf->fill + len + 1) >= buf->size) { - if (!buf_grow(buf, len + 1)) - return 0; - } - - memcpy(buf->dptr, s, len); - buf->fill += len; - buf->dptr += len; - - *(buf->dptr) = 0; - - return len; -} - -/* read buffer length */ -static int buf_length(struct template_buffer *buf) -{ - return buf->fill; -} - -/* destroy buffer object and return pointer to data */ -static char *buf_destroy(struct template_buffer *buf) -{ - char *data = buf->data; - - free(buf); - return data; -} - -static void luastr_escape(struct template_buffer *out, const char *s, unsigned int l, - int escape_xml) -{ - int esl; - char esq[8]; - char *ptr; - - for (ptr = (char *)s; ptr < (s + l); ptr++) { - switch (*ptr) { - case '\\': - buf_append(out, "\\\\", 2); - break; - - case '"': - if (escape_xml) - buf_append(out, """, 5); - else - buf_append(out, "\\\"", 2); - break; - - case '\n': - buf_append(out, "\\n", 2); - break; - - case '\'': - case '&': - case '<': - case '>': - if (escape_xml) { - esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr); - buf_append(out, esq, esl); - break; - } - - default: - buf_putchar(out, *ptr); - } - } -} - -/* Simple strstr() like function that takes len arguments for both haystack and needle. */ -static char *strfind(char *haystack, int hslen, const char *needle, int ndlen) -{ - int match = 0; - int i, j; - - for( i = 0; i < hslen; i++ ) { - if( haystack[i] == needle[0] ) { - match = ((ndlen == 1) || ((i + ndlen) <= hslen)); - - for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ ) { - if( haystack[i+j] != needle[j] ) { - match = 0; - break; - } - } - - if( match ) - return &haystack[i]; - } - } - - return NULL; -} - - -static int template_error(lua_State *L, struct template_parser *parser) -{ - const char *err = luaL_checkstring(L, -1); - const char *off = parser->prv_chunk.s; - const char *ptr; - char msg[1024]; - int line = 0; - int chunkline = 0; - - if ((ptr = strfind((char *)err, strlen(err), "]:", 2)) != NULL) { - chunkline = atoi(ptr + 2) - parser->prv_chunk.line; - - while (*ptr) { - if (*ptr++ == ' ') { - err = ptr; - break; - } - } - } - - if (strfind((char *)err, strlen(err), "'char(27)'", 10) != NULL) { - off = parser->data + parser->size; - err = "'%>' expected before end of file"; - chunkline = 0; - } - - for (ptr = parser->data; ptr < off; ptr++) - if (*ptr == '\n') - line++; - - snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s", - parser->file ? parser->file : "[string]", line + chunkline, err ? err : "(unknown error)"); - - lua_pushnil(L); - lua_pushinteger(L, line + chunkline); - lua_pushstring(L, msg); - - return 3; -} - - -static void template_close(struct template_parser *parser) -{ - if (!parser) - return; - - if (parser->gc != NULL) - free(parser->gc); - - /* if file is not set, we were parsing a string */ - if (parser->file) { - if ((parser->data != NULL) && (parser->data != MAP_FAILED)) - munmap(parser->data, parser->size); - - if (parser->fd >= 0) - close(parser->fd); - } - - free(parser); -} - -static void template_text(struct template_parser *parser, const char *e) -{ - const char *s = parser->off; - - if (s < (parser->data + parser->size)) { - if (parser->strip_after) { - while ((s <= e) && isspace(*s)) - s++; - } - - parser->cur_chunk.type = T_TYPE_TEXT; - } else { - parser->cur_chunk.type = T_TYPE_EOF; - } - - parser->cur_chunk.line = parser->line; - parser->cur_chunk.s = s; - parser->cur_chunk.e = e; -} - -static void template_code(struct template_parser *parser, const char *e) -{ - const char *s = parser->off; - - parser->strip_before = 0; - parser->strip_after = 0; - - if (*s == '-') { - parser->strip_before = 1; - for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++); - } - - if (*(e-1) == '-') { - parser->strip_after = 1; - for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--); - } - - switch (*s) { - /* comment */ - case '#': - s++; - parser->cur_chunk.type = T_TYPE_COMMENT; - break; - - /* include */ - case '+': - s++; - parser->cur_chunk.type = T_TYPE_INCLUDE; - break; - - /* expr */ - case '=': - s++; - parser->cur_chunk.type = T_TYPE_EXPR; - break; - - /* code */ - default: - parser->cur_chunk.type = T_TYPE_CODE; - break; - } - - parser->cur_chunk.line = parser->line; - parser->cur_chunk.s = s; - parser->cur_chunk.e = e; -} - - - -static const char *template_format_chunk(struct template_parser *parser, size_t *sz) -{ - const char *s, *p; - const char *head, *tail; - struct template_chunk *c = &parser->prv_chunk; - struct template_buffer *buf; - - *sz = 0; - s = parser->gc = NULL; - - if (parser->strip_before && c->type == T_TYPE_TEXT) { - while ((c->e > c->s) && isspace(*(c->e - 1))) - c->e--; - } - - /* empty chunk */ - if (c->s == c->e) { - if (c->type == T_TYPE_EOF) { - *sz = 0; - s = NULL; - } else { - *sz = 1; - s = " "; - } - } else if ((buf = buf_init(c->e - c->s)) != NULL) { /* format chunk */ - if ((head = gen_code[c->type][0]) != NULL) - buf_append(buf, head, strlen(head)); - - switch (c->type) { - case T_TYPE_TEXT: - luastr_escape(buf, c->s, c->e - c->s, 0); - break; - - case T_TYPE_EXPR: - buf_append(buf, c->s, c->e - c->s); - for (p = c->s; p < c->e; p++) - parser->line += (*p == '\n'); - break; - - case T_TYPE_INCLUDE: - luastr_escape(buf, c->s, c->e - c->s, 0); - break; - - case T_TYPE_CODE: - buf_append(buf, c->s, c->e - c->s); - for (p = c->s; p < c->e; p++) - parser->line += (*p == '\n'); - break; - } - - if ((tail = gen_code[c->type][1]) != NULL) - buf_append(buf, tail, strlen(tail)); - - *sz = buf_length(buf); - s = parser->gc = buf_destroy(buf); - - if (!*sz) { - *sz = 1; - s = " "; - } - } - - return s; -} - -static const char *template_reader(lua_State *L, void *ud, size_t *sz) -{ - struct template_parser *parser = ud; - int rem = parser->size - (parser->off - parser->data); - char *tag; - - parser->prv_chunk = parser->cur_chunk; - - /* free previous string */ - if (parser->gc) { - free(parser->gc); - parser->gc = NULL; - } - - /* before tag */ - if (!parser->in_expr) { - if ((tag = strfind(parser->off, rem, "<%", 2)) != NULL) { - template_text(parser, tag); - parser->off = tag + 2; - parser->in_expr = 1; - } else { - template_text(parser, parser->data + parser->size); - parser->off = parser->data + parser->size; - } - } else { /* inside tag */ - if ((tag = strfind(parser->off, rem, "%>", 2)) != NULL) { - template_code(parser, tag); - parser->off = tag + 2; - parser->in_expr = 0; - } else { - /* unexpected EOF */ - template_code(parser, parser->data + parser->size); - - *sz = 1; - return "\033"; - } - } - - return template_format_chunk(parser, sz); -} - -static int template_L_do_parse(lua_State *L, struct template_parser *parser, const char *chunkname) -{ - int lua_status, rv; - - if (!parser) { - lua_pushnil(L); - lua_pushinteger(L, errno); - lua_pushstring(L, strerror(errno)); - return 3; - } - -#if LUA_VERSION_NUM > 501 - lua_status = lua_load(L, template_reader, parser, chunkname, NULL); -#else - lua_status = lua_load(L, template_reader, parser, chunkname); -#endif - if (lua_status == 0) - rv = 1; - else - rv = template_error(L, parser); - - template_close(parser); - - return rv; -} - -struct template_parser * template_open(const char *file) -{ - struct stat s; - struct template_parser *parser; - - if (!(parser = malloc(sizeof(*parser)))) - goto err; - - memset(parser, 0, sizeof(*parser)); - parser->fd = -1; - parser->file = file; - - if (stat(file, &s)) - goto err; - - if ((parser->fd = open(file, O_RDONLY)) < 0) - goto err; - - parser->size = s.st_size; - parser->data = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE, - parser->fd, 0); - - if (parser->data != MAP_FAILED) { - parser->off = parser->data; - parser->cur_chunk.type = T_TYPE_INIT; - parser->cur_chunk.s = parser->data; - parser->cur_chunk.e = parser->data; - - return parser; - } - -err: - template_close(parser); - return NULL; -} - -static void template_read_cb(struct ev_loop *loop, ev_io *w, int revents) -{ - struct uh_connection *con = container_of(w, struct uh_connection, read_watcher_lua); - char buf[1024] = ""; - int len; - - len = read(w->fd, buf, sizeof(buf)); - if (len > 0) - uh_send_chunk(con, buf, len); - else if (len == 0) { - uh_send_chunk(con, NULL, 0); - close(w->fd); - ev_io_stop(con->srv->loop, w); - - if (!(con->flags & UH_CON_CLOSE)) - con->flags |= UH_CON_REUSE; - } -} - -static void child_cb(struct ev_loop *loop, ev_child *w, int revents) -{ - struct uh_connection *con = container_of(w, struct uh_connection, child_watcher); - ev_child_stop(con->srv->loop, w); -} - -void uh_template(struct uh_connection *con) -{ - struct template_parser *parser; - lua_State *L = con->srv->L; - pid_t pid; - int pipefd[2]; - static char path[PATH_MAX] = ""; - struct stat st; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - struct uh_str *ustr; - - strcpy(path, con->srv->docroot); - strncat(path, con->req.path.at, con->req.path.len); - - if (stat(path, &st) < 0) { - uh_send_error(con, HTTP_STATUS_NOT_FOUND, NULL); - return; - } - - uh_log_debug("Path:%s", path); - - - if (!L) { - L = luaL_newstate(); - if (!L) { - uh_log_err("cannot create LUA state: not enough memory"); - goto err; - } - - con->srv->L = L; - luaL_openlibs(L); - } - - /* - * Add all variables to the global environment of LUA. - * eg. <%=_UHTTP["REMOTE_HOST"]%> - */ - - lua_newtable(L); - - lua_pushstring(L, uh_get_method_str(con)); - lua_setfield(L, -2, "HTTP_METHOD"); - - getpeername(con->sock, (struct sockaddr *)&addr, &addrlen); - lua_pushstring(L, inet_ntoa(addr.sin_addr)); - lua_setfield(L, -2, "REMOTE_HOST"); - - ustr = uh_get_url(con); - lua_pushlstring(L, ustr->at, ustr->len); - lua_setfield(L, -2, "HTTP_URL"); - - ustr = uh_get_path(con); - lua_pushlstring(L, ustr->at, ustr->len); - lua_setfield(L, -2, "HTTP_PATH"); - - lua_setglobal(L, "_UHTTP"); - - if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) < 0) { - uh_log_err("pipe"); - goto err; - } - - uh_send_head(con, HTTP_STATUS_OK, -1, "Pragma: no-cache\r\nCache-Control: no-cache\r\n"); - - pid = fork(); - switch (pid) { - case -1: - uh_log_err("fork"); - goto err; - break; - - case 0: - close(0); - close(1); - close(pipefd[0]); - dup2(pipefd[1], 1); - - parser = template_open(path); - if (!parser) { - uh_log_err("template_open failed"); - return; - } - - - if ((template_L_do_parse(L, parser, path) != 1) || lua_pcall(L, 0, 0, 0)) { - uh_printf_chunk(con, "

Lua Error

\n%s\n", lua_tostring(L, -1)); - uh_printf_chunk(con, "\n"); - uh_send_chunk(con, NULL, 0); - lua_pop(L, -1); - } else { - uh_send_chunk(con, NULL, 0); - } - exit(0); - break; - - default: - close(pipefd[1]); - ev_io_init(&con->read_watcher_lua, template_read_cb, pipefd[0], EV_READ); - ev_io_start(con->srv->loop, &con->read_watcher_lua); - - ev_child_init(&con->child_watcher, child_cb, pid, 0); - ev_child_start(con->srv->loop, &con->child_watcher); - break; - } - - return; -err: - uh_send_error(con, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); -} +/* + * Copyright (C) 2017 Jianhui Zhao + * + * based on https://git.lede-project.org/project/luci.git modules/luci-base/src/template_parser.c + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" +#include "uhttp/uhttp.h" + +/* code types */ +#define T_TYPE_INIT 0 +#define T_TYPE_TEXT 1 +#define T_TYPE_COMMENT 2 +#define T_TYPE_EXPR 3 +#define T_TYPE_INCLUDE 4 +#define T_TYPE_CODE 5 +#define T_TYPE_EOF 6 + +/* leading and trailing code for different types */ +static const char *gen_code[9][2] = { + { NULL, NULL }, /* T_TYPE_INIT */ + { "io.write(\"", "\")" }, /* T_TYPE_TEXT */ + { NULL, NULL }, /* T_TYPE_COMMENT */ + { "io.write(tostring(", " or \"\"))" }, /* T_TYPE_EXPR */ + { "include(\"", "\")" }, /* T_TYPE_INCLUDE */ + { NULL, " " }, /* T_TYPE_CODE */ + { NULL, NULL }, /* T_TYPE_EOF */ +}; + +/* buffer object */ +struct template_buffer { + char *data; + char *dptr; + unsigned int size; + unsigned int fill; +}; + +struct template_chunk { + const char *s; + const char *e; + int type; + int line; +}; + +/* parser state */ +struct template_parser { + int fd; + uint32_t size; + char *data; + char *off; + char *gc; + int line; + int in_expr; + int strip_before; + int strip_after; + struct template_chunk prv_chunk; + struct template_chunk cur_chunk; + const char *file; +}; + +/* initialize a buffer object */ +struct template_buffer *buf_init(int size) +{ + struct template_buffer *buf; + + if (size <= 0) + size = 1024; + + buf = (struct template_buffer *)malloc(sizeof(struct template_buffer)); + + if (buf != NULL) + { + buf->fill = 0; + buf->size = size; + buf->data = malloc(buf->size); + + if (buf->data != NULL) { + buf->dptr = buf->data; + buf->data[0] = 0; + return buf; + } + free(buf); + } + + return NULL; +} + +/* grow buffer */ +static int buf_grow(struct template_buffer *buf, int size) +{ + unsigned int off = (buf->dptr - buf->data); + char *data; + + if (size <= 0) + size = 1024; + + data = realloc(buf->data, buf->size + size); + + if (data != NULL) { + buf->data = data; + buf->dptr = data + off; + buf->size += size; + return buf->size; + } + + return 0; +} + +/* put one char into buffer object */ +static int buf_putchar(struct template_buffer *buf, char c) +{ + if( ((buf->fill + 1) >= buf->size) && !buf_grow(buf, 0) ) + return 0; + + *(buf->dptr++) = c; + *(buf->dptr) = 0; + + buf->fill++; + return 1; +} + +/* append data to buffer */ +static int buf_append(struct template_buffer *buf, const char *s, int len) +{ + if ((buf->fill + len + 1) >= buf->size) { + if (!buf_grow(buf, len + 1)) + return 0; + } + + memcpy(buf->dptr, s, len); + buf->fill += len; + buf->dptr += len; + + *(buf->dptr) = 0; + + return len; +} + +/* read buffer length */ +static int buf_length(struct template_buffer *buf) +{ + return buf->fill; +} + +/* destroy buffer object and return pointer to data */ +static char *buf_destroy(struct template_buffer *buf) +{ + char *data = buf->data; + + free(buf); + return data; +} + +static void luastr_escape(struct template_buffer *out, const char *s, unsigned int l, + int escape_xml) +{ + int esl; + char esq[8]; + char *ptr; + + for (ptr = (char *)s; ptr < (s + l); ptr++) { + switch (*ptr) { + case '\\': + buf_append(out, "\\\\", 2); + break; + + case '"': + if (escape_xml) + buf_append(out, """, 5); + else + buf_append(out, "\\\"", 2); + break; + + case '\n': + buf_append(out, "\\n", 2); + break; + + case '\'': + case '&': + case '<': + case '>': + if (escape_xml) { + esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr); + buf_append(out, esq, esl); + break; + } + + default: + buf_putchar(out, *ptr); + } + } +} + +/* Simple strstr() like function that takes len arguments for both haystack and needle. */ +static char *strfind(char *haystack, int hslen, const char *needle, int ndlen) +{ + int match = 0; + int i, j; + + for( i = 0; i < hslen; i++ ) { + if( haystack[i] == needle[0] ) { + match = ((ndlen == 1) || ((i + ndlen) <= hslen)); + + for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ ) { + if( haystack[i+j] != needle[j] ) { + match = 0; + break; + } + } + + if( match ) + return &haystack[i]; + } + } + + return NULL; +} + + +static int template_error(lua_State *L, struct template_parser *parser) +{ + const char *err = luaL_checkstring(L, -1); + const char *off = parser->prv_chunk.s; + const char *ptr; + char msg[1024]; + int line = 0; + int chunkline = 0; + + if ((ptr = strfind((char *)err, strlen(err), "]:", 2)) != NULL) { + chunkline = atoi(ptr + 2) - parser->prv_chunk.line; + + while (*ptr) { + if (*ptr++ == ' ') { + err = ptr; + break; + } + } + } + + if (strfind((char *)err, strlen(err), "'char(27)'", 10) != NULL) { + off = parser->data + parser->size; + err = "'%>' expected before end of file"; + chunkline = 0; + } + + for (ptr = parser->data; ptr < off; ptr++) + if (*ptr == '\n') + line++; + + snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s", + parser->file ? parser->file : "[string]", line + chunkline, err ? err : "(unknown error)"); + + lua_pushnil(L); + lua_pushinteger(L, line + chunkline); + lua_pushstring(L, msg); + + return 3; +} + + +static void template_close(struct template_parser *parser) +{ + if (!parser) + return; + + if (parser->gc != NULL) + free(parser->gc); + + /* if file is not set, we were parsing a string */ + if (parser->file) { + if ((parser->data != NULL) && (parser->data != MAP_FAILED)) + munmap(parser->data, parser->size); + + if (parser->fd >= 0) + close(parser->fd); + } + + free(parser); +} + +static void template_text(struct template_parser *parser, const char *e) +{ + const char *s = parser->off; + + if (s < (parser->data + parser->size)) { + if (parser->strip_after) { + while ((s <= e) && isspace(*s)) + s++; + } + + parser->cur_chunk.type = T_TYPE_TEXT; + } else { + parser->cur_chunk.type = T_TYPE_EOF; + } + + parser->cur_chunk.line = parser->line; + parser->cur_chunk.s = s; + parser->cur_chunk.e = e; +} + +static void template_code(struct template_parser *parser, const char *e) +{ + const char *s = parser->off; + + parser->strip_before = 0; + parser->strip_after = 0; + + if (*s == '-') { + parser->strip_before = 1; + for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++); + } + + if (*(e-1) == '-') { + parser->strip_after = 1; + for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--); + } + + switch (*s) { + /* comment */ + case '#': + s++; + parser->cur_chunk.type = T_TYPE_COMMENT; + break; + + /* include */ + case '+': + s++; + parser->cur_chunk.type = T_TYPE_INCLUDE; + break; + + /* expr */ + case '=': + s++; + parser->cur_chunk.type = T_TYPE_EXPR; + break; + + /* code */ + default: + parser->cur_chunk.type = T_TYPE_CODE; + break; + } + + parser->cur_chunk.line = parser->line; + parser->cur_chunk.s = s; + parser->cur_chunk.e = e; +} + + + +static const char *template_format_chunk(struct template_parser *parser, size_t *sz) +{ + const char *s, *p; + const char *head, *tail; + struct template_chunk *c = &parser->prv_chunk; + struct template_buffer *buf; + + *sz = 0; + s = parser->gc = NULL; + + if (parser->strip_before && c->type == T_TYPE_TEXT) { + while ((c->e > c->s) && isspace(*(c->e - 1))) + c->e--; + } + + /* empty chunk */ + if (c->s == c->e) { + if (c->type == T_TYPE_EOF) { + *sz = 0; + s = NULL; + } else { + *sz = 1; + s = " "; + } + } else if ((buf = buf_init(c->e - c->s)) != NULL) { /* format chunk */ + if ((head = gen_code[c->type][0]) != NULL) + buf_append(buf, head, strlen(head)); + + switch (c->type) { + case T_TYPE_TEXT: + luastr_escape(buf, c->s, c->e - c->s, 0); + break; + + case T_TYPE_EXPR: + buf_append(buf, c->s, c->e - c->s); + for (p = c->s; p < c->e; p++) + parser->line += (*p == '\n'); + break; + + case T_TYPE_INCLUDE: + luastr_escape(buf, c->s, c->e - c->s, 0); + break; + + case T_TYPE_CODE: + buf_append(buf, c->s, c->e - c->s); + for (p = c->s; p < c->e; p++) + parser->line += (*p == '\n'); + break; + } + + if ((tail = gen_code[c->type][1]) != NULL) + buf_append(buf, tail, strlen(tail)); + + *sz = buf_length(buf); + s = parser->gc = buf_destroy(buf); + + if (!*sz) { + *sz = 1; + s = " "; + } + } + + return s; +} + +static const char *template_reader(lua_State *L, void *ud, size_t *sz) +{ + struct template_parser *parser = ud; + int rem = parser->size - (parser->off - parser->data); + char *tag; + + parser->prv_chunk = parser->cur_chunk; + + /* free previous string */ + if (parser->gc) { + free(parser->gc); + parser->gc = NULL; + } + + /* before tag */ + if (!parser->in_expr) { + if ((tag = strfind(parser->off, rem, "<%", 2)) != NULL) { + template_text(parser, tag); + parser->off = tag + 2; + parser->in_expr = 1; + } else { + template_text(parser, parser->data + parser->size); + parser->off = parser->data + parser->size; + } + } else { /* inside tag */ + if ((tag = strfind(parser->off, rem, "%>", 2)) != NULL) { + template_code(parser, tag); + parser->off = tag + 2; + parser->in_expr = 0; + } else { + /* unexpected EOF */ + template_code(parser, parser->data + parser->size); + + *sz = 1; + return "\033"; + } + } + + return template_format_chunk(parser, sz); +} + +static int template_L_do_parse(lua_State *L, struct template_parser *parser, const char *chunkname) +{ + int lua_status, rv; + + if (!parser) { + lua_pushnil(L); + lua_pushinteger(L, errno); + lua_pushstring(L, strerror(errno)); + return 3; + } + +#if LUA_VERSION_NUM > 501 + lua_status = lua_load(L, template_reader, parser, chunkname, NULL); +#else + lua_status = lua_load(L, template_reader, parser, chunkname); +#endif + if (lua_status == 0) + rv = 1; + else + rv = template_error(L, parser); + + template_close(parser); + + return rv; +} + +struct template_parser * template_open(const char *file) +{ + struct stat s; + struct template_parser *parser; + + if (!(parser = malloc(sizeof(*parser)))) + goto err; + + memset(parser, 0, sizeof(*parser)); + parser->fd = -1; + parser->file = file; + + if (stat(file, &s)) + goto err; + + if ((parser->fd = open(file, O_RDONLY)) < 0) + goto err; + + parser->size = s.st_size; + parser->data = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE, + parser->fd, 0); + + if (parser->data != MAP_FAILED) { + parser->off = parser->data; + parser->cur_chunk.type = T_TYPE_INIT; + parser->cur_chunk.s = parser->data; + parser->cur_chunk.e = parser->data; + + return parser; + } + +err: + template_close(parser); + return NULL; +} + +static void template_read_cb(struct ev_loop *loop, ev_io *w, int revents) +{ + struct uh_connection *con = container_of(w, struct uh_connection, read_watcher_lua); + char buf[1024] = ""; + int len; + + len = read(w->fd, buf, sizeof(buf)); + if (len > 0) + uh_send_chunk(con, buf, len); + else if (len == 0) { + uh_send_chunk(con, NULL, 0); + close(w->fd); + ev_io_stop(con->srv->loop, w); + + if (!(con->flags & UH_CON_CLOSE)) + con->flags |= UH_CON_REUSE; + } +} + +static void child_cb(struct ev_loop *loop, ev_child *w, int revents) +{ + struct uh_connection *con = container_of(w, struct uh_connection, child_watcher); + ev_child_stop(con->srv->loop, w); +} + +void uh_template(struct uh_connection *con) +{ + struct template_parser *parser; + lua_State *L = con->srv->L; + pid_t pid; + int pipefd[2]; + static char path[PATH_MAX] = ""; + struct stat st; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + struct uh_str *ustr; + + strcpy(path, con->srv->docroot); + strncat(path, con->req.path.at, con->req.path.len); + + if (stat(path, &st) < 0) { + uh_send_error(con, HTTP_STATUS_NOT_FOUND, NULL); + return; + } + + uh_log_debug("Path:%s", path); + + + if (!L) { + L = luaL_newstate(); + if (!L) { + uh_log_err("cannot create LUA state: not enough memory"); + goto err; + } + + con->srv->L = L; + luaL_openlibs(L); + } + + /* + * Add all variables to the global environment of LUA. + * eg. <%=_UHTTP["REMOTE_HOST"]%> + */ + + lua_newtable(L); + + lua_pushstring(L, uh_get_method_str(con)); + lua_setfield(L, -2, "HTTP_METHOD"); + + getpeername(con->sock, (struct sockaddr *)&addr, &addrlen); + lua_pushstring(L, inet_ntoa(addr.sin_addr)); + lua_setfield(L, -2, "REMOTE_HOST"); + + ustr = uh_get_url(con); + lua_pushlstring(L, ustr->at, ustr->len); + lua_setfield(L, -2, "HTTP_URL"); + + ustr = uh_get_path(con); + lua_pushlstring(L, ustr->at, ustr->len); + lua_setfield(L, -2, "HTTP_PATH"); + + lua_setglobal(L, "_UHTTP"); + + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) < 0) { + uh_log_err("pipe"); + goto err; + } + + uh_send_head(con, HTTP_STATUS_OK, -1, "Pragma: no-cache\r\nCache-Control: no-cache\r\n"); + + pid = fork(); + switch (pid) { + case -1: + uh_log_err("fork"); + goto err; + break; + + case 0: + close(0); + close(1); + close(pipefd[0]); + dup2(pipefd[1], 1); + + parser = template_open(path); + if (!parser) { + uh_log_err("template_open failed"); + return; + } + + + if ((template_L_do_parse(L, parser, path) != 1) || lua_pcall(L, 0, 0, 0)) { + uh_printf_chunk(con, "

Lua Error

\n%s\n", lua_tostring(L, -1)); + uh_printf_chunk(con, "\n"); + uh_send_chunk(con, NULL, 0); + lua_pop(L, -1); + } else { + uh_send_chunk(con, NULL, 0); + } + exit(0); + break; + + default: + close(pipefd[1]); + ev_io_init(&con->read_watcher_lua, template_read_cb, pipefd[0], EV_READ); + ev_io_start(con->srv->loop, &con->read_watcher_lua); + + ev_child_init(&con->child_watcher, child_cb, pid, 0); + ev_child_start(con->srv->loop, &con->child_watcher); + break; + } + + return; +err: + uh_send_error(con, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); +}