added mod_cml (Cache Meta Language)

git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.3.x@421 152afb58-edef-0310-8abb-c4023f1b3aa9
svn/tags/lighttpd-1.3.15
Jan Kneschke 18 years ago
parent 288b0b51e7
commit 81368ccd4e

@ -68,6 +68,11 @@ src += $(common_src)
common_libadd =
endif
lib_LTLIBRARIES += mod_cml.la
mod_cml_la_SOURCES = mod_cml.c mod_cml_funcs.c mod_cml_logic.c
mod_cml_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
mod_cml_la_LIBADD = $(MYSQL_LIBS) $(common_libadd)
lib_LTLIBRARIES += mod_mysql_vhost.la
mod_mysql_vhost_la_SOURCES = mod_mysql_vhost.c
mod_mysql_vhost_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
@ -195,11 +200,7 @@ hdr = server.h buffer.h network.h log.h keyvalue.h \
network_backends.h configfile.h bitset.h \
mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \
configparser.h mod_ssi_exprparser.h \
sys-mmap.h sys-socket.h
# license.h
#hdr += chat.h chat_misc.h chat_endec.h chat_user.h \
# chat_channel.h
sys-mmap.h sys-socket.h mod_cml.h
DEFS= @DEFS@ -DLIBRARY_DIR="\"$(libdir)\""

@ -0,0 +1,329 @@
#include <sys/stat.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include "buffer.h"
#include "server.h"
#include "log.h"
#include "plugin.h"
#include "response.h"
#include "stream.h"
#include "mod_cml.h"
/* init the plugin data */
INIT_FUNC(mod_cache_init) {
plugin_data *p;
p = calloc(1, sizeof(*p));
p->basedir = buffer_init();
return p;
}
/* detroy the plugin data */
FREE_FUNC(mod_cache_free) {
plugin_data *p = p_d;
UNUSED(srv);
if (!p) return HANDLER_GO_ON;
if (p->config_storage) {
size_t i;
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s = p->config_storage[i];
buffer_free(s->ext);
free(s);
}
free(p->config_storage);
}
buffer_free(p->basedir);
free(p);
return HANDLER_GO_ON;
}
/* handle plugin config and check values */
SETDEFAULTS_FUNC(mod_cache_set_defaults) {
plugin_data *p = p_d;
size_t i = 0;
config_values_t cv[] = {
{ "cache.extension", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
if (!p) return HANDLER_ERROR;
p->config_storage = malloc(srv->config_context->used * sizeof(specific_config *));
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s;
s = malloc(sizeof(plugin_config));
s->ext = buffer_init();
cv[0].destination = s->ext;
p->config_storage[i] = s;
if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
return HANDLER_ERROR;
}
}
return HANDLER_GO_ON;
}
#define PATCH(x) \
p->conf.x = s->x;
static int mod_cache_patch_connection(server *srv, connection *con, plugin_data *p, const char *stage, size_t stage_len) {
size_t i, j;
/* skip the first, the global context */
for (i = 1; i < srv->config_context->used; i++) {
data_config *dc = (data_config *)srv->config_context->data[i];
plugin_config *s = p->config_storage[i];
/* not our stage */
if (!buffer_is_equal_string(dc->comp_key, stage, stage_len)) continue;
/* condition didn't match */
if (!config_check_cond(srv, con, dc)) continue;
/* merge config */
for (j = 0; j < dc->value->used; j++) {
data_unset *du = dc->value->data[j];
if (buffer_is_equal_string(du->key, CONST_STR_LEN("cache.extension"))) {
PATCH(ext);
}
}
}
return 0;
}
static int mod_cache_setup_connection(server *srv, connection *con, plugin_data *p) {
plugin_config *s = p->config_storage[0];
UNUSED(srv);
UNUSED(con);
PATCH(ext);
return 0;
}
#undef PATCH
int cache_get_cookie_session_id(server *srv, connection *con, plugin_data *p) {
data_unset *d;
if (NULL != (d = array_get_element(con->request.headers, "Cookie"))) {
data_string *ds = (data_string *)d;
size_t key = 0, value = 0;
size_t is_key = 1, is_sid = 0;
size_t i;
/* found COOKIE */
if (!DATA_IS_STRING(d)) return -1;
if (ds->value->used == 0) return -1;
if (ds->value->ptr[0] == '\0' ||
ds->value->ptr[0] == '=' ||
ds->value->ptr[0] == ';') return -1;
buffer_reset(p->session_id);
for (i = 0; i < ds->value->used; i++) {
switch(ds->value->ptr[i]) {
case '=':
if (is_key) {
if (0 == strncmp(ds->value->ptr + key, "PHPSESSID", i - key)) {
/* found PHP-session-id-key */
is_sid = 1;
}
value = i + 1;
is_key = 0;
}
break;
case ';':
if (is_sid) {
buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
}
is_sid = 0;
key = i + 1;
value = 0;
is_key = 1;
break;
case ' ':
if (is_key == 1 && key == i) key = i + 1;
if (is_key == 0 && value == i) value = i + 1;
break;
case '\0':
if (is_sid) {
buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
}
/* fin */
break;
}
}
if (!buffer_is_empty(p->session_id)) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"Session-ID", p->session_id);
}
}
return !buffer_is_empty(p->session_id);
}
int cache_get_url_session_id(server *srv, connection *con, plugin_data *p) {
size_t key = 0, value = 0;
size_t is_key = 1, is_sid = 0;
size_t i;
buffer_reset(p->session_id);
for (i = 0; i < con->uri.query->used; i++) {
switch(con->uri.query->ptr[i]) {
case '=':
if (is_key) {
if (0 == strncmp(con->uri.query->ptr + key, "PHPSESSID", i - key)) {
/* found PHP-session-id-key */
is_sid = 1;
}
value = i + 1;
is_key = 0;
}
break;
case '&':
if (is_sid) {
buffer_copy_string_len(p->session_id, con->uri.query->ptr + value, i - value);
}
is_sid = 0;
key = i + 1;
value = 0;
is_key = 1;
break;
case ' ':
if (is_key == 1 && key == i) key = i + 1;
if (is_key == 0 && value == i) value = i + 1;
break;
case '\0':
if (is_sid) {
buffer_copy_string_len(p->session_id, con->uri.query->ptr + value, i - value);
}
/* fin */
break;
}
}
if (!buffer_is_empty(p->session_id)) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"Session-ID", p->session_id);
}
return !buffer_is_empty(p->session_id);
}
int cache_get_session_id(server *srv, connection *con, plugin_data *p) {
return cache_get_cookie_session_id(srv, con, p) ||
cache_get_url_session_id(srv, con, p);
}
URIHANDLER_FUNC(mod_cache_is_handled) {
int ct_len, s_len;
buffer *b;
char *c;
buffer *fn = con->physical.path;
plugin_data *p = p_d;
size_t i;
if (fn->used == 0) return HANDLER_ERROR;
mod_cache_setup_connection(srv, con, p);
for (i = 0; i < srv->config_patches->used; i++) {
buffer *patch = srv->config_patches->ptr[i];
mod_cache_patch_connection(srv, con, p, CONST_BUF_LEN(patch));
}
if (buffer_is_empty(p->conf.ext)) return HANDLER_GO_ON;
ct_len = p->conf.ext->used - 1;
s_len = fn->used - 1;
if (s_len < ct_len) return HANDLER_GO_ON;
if (0 != strncmp(fn->ptr + s_len - ct_len, p->conf.ext->ptr, ct_len)) {
/* not my job */
return HANDLER_GO_ON;
}
/* cleanup basedir */
b = p->basedir;
buffer_copy_string_buffer(b, fn);
for (c = b->ptr + b->used - 1; c > b->ptr && *c != '/'; c--);
if (*c == '/') {
b->used = c - b->ptr + 2;
*(c+1) = '\0';
}
/* prepare variables
* - session-id
* - cookie-based
* - get-param-based
*/
cache_get_session_id(srv, con, p);
switch(cache_parse(srv, con, p, fn)) {
case -1:
con->http_status = 500;
return HANDLER_COMEBACK;
case 0:
buffer_reset(con->physical.path);
return HANDLER_FINISHED;
case 1:
return HANDLER_COMEBACK;
}
return 0;
}
int mod_cache_plugin_init(plugin *p) {
p->version = LIGHTTPD_VERSION_ID;
p->name = buffer_init_string("cache");
p->init = mod_cache_init;
p->cleanup = mod_cache_free;
p->set_defaults = mod_cache_set_defaults;
p->handle_subrequest_start = mod_cache_is_handled;
p->data = NULL;
return 0;
}

@ -0,0 +1,109 @@
#include <sys/stat.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include "buffer.h"
#include "server.h"
#include "log.h"
#include "plugin.h"
#include "response.h"
#include "mod_cml.h"
CACHE_FUNC_PROTO(f_unix_time_now) {
UNUSED(srv);
UNUSED(con);
UNUSED(p);
VAL_LONG(result) = srv->cur_ts;
return 0;
}
CACHE_FUNC_PROTO(f_file_mtime) {
buffer *b;
struct stat st;
UNUSED(con);
if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) {
fprintf(stderr, "%s.%d: f_file_mtime: I need a string: %d\n",
__FILE__, __LINE__, p->params->ptr[0]->type);
return -1;
}
b = buffer_init();
/* build filename */
buffer_copy_string_buffer(b, p->basedir);
buffer_append_string_buffer(b, p->params->ptr[0]->data.str);
if (-1 == stat(b->ptr, &st)) {
log_error_write(srv, __FILE__, __LINE__, "sbs", "trigger.if file.mtime():", b, strerror(errno));
buffer_free(b);
return -1;
}
buffer_free(b);
tnode_prepare_long(result);
VAL_LONG(result) = st.st_mtime;
return 0;
}
CACHE_FUNC_PROTO(f_mysql_escape) {
UNUSED(srv);
UNUSED(con);
if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) {
fprintf(stderr, "%s.%d: f_mysql_escape: I need a string: %d\n",
__FILE__, __LINE__, p->params->ptr[0]->type);
return -1;
}
tnode_prepare_string(result);
buffer_copy_string_buffer(VAL_STRING(result), p->params->ptr[0]->data.str);
return 0;
}
CACHE_FUNC_PROTO(f_mysql_query) {
UNUSED(srv);
UNUSED(con);
if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) {
fprintf(stderr, "%s.%d: f_mysql_escape: I need a string: %d\n",
__FILE__, __LINE__, p->params->ptr[0]->type);
return -1;
}
tnode_prepare_long(result);
VAL_LONG(result) = 0;
return 0;
}
CACHE_FUNC_PROTO(f_mysql_connect) {
UNUSED(srv);
UNUSED(con);
if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) return -1;
if (p->params->ptr[1]->type != T_NODE_VALUE_STRING) return -1;
if (p->params->ptr[2]->type != T_NODE_VALUE_STRING) return -1;
if (p->params->ptr[3]->type != T_NODE_VALUE_STRING) return -1;
tnode_prepare_long(result);
VAL_LONG(result) = 0;
return 0;
}

@ -0,0 +1,773 @@
#include <sys/stat.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include "buffer.h"
#include "server.h"
#include "log.h"
#include "plugin.h"
#include "mod_cml.h"
#include "stream.h"
tnode_val *tnode_val_init() {
tnode_val *tv;
tv = calloc(1, sizeof(*tv));
assert(tv);
return tv;
}
int tnode_val_move(tnode_val *dst, tnode_val *src) {
switch(src->type) {
case T_NODE_VALUE_LONG:
dst->data.lon = src->data.lon;
break;
case T_NODE_VALUE_STRING:
dst->data.str = src->data.str;
src->data.str = NULL;
break;
default:
break;
}
dst->type = src->type;
src->type = UNSET;
return 0;
}
void tnode_val_free(tnode_val *tv) {
switch(tv->type) {
case T_NODE_VALUE_STRING:
buffer_free(tv->data.str);
break;
default:
break;
}
free(tv);
}
tnode_val_array *tnode_val_array_init() {
tnode_val_array *tva;
tva = calloc(1, sizeof(*tva));
assert(tva);
return tva;
}
int tnode_val_array_append(tnode_val_array *tva, tnode_val *val) {
if (tva->size == 0) {
tva->size = 4;
tva->ptr = malloc(tva->size * sizeof(tnode_val *));
} else if (tva->size == tva->used) {
tva->size += 4;
tva->ptr = realloc(tva->ptr, tva->size * sizeof(tnode_val *));
}
tva->ptr[tva->used++] = val;
return 0;
}
void tnode_val_array_free(tnode_val_array *tva) {
size_t i;
if (!tva) return;
for (i = 0; i < tva->used; i++) {
tnode_val_free(tva->ptr[i]);
}
free(tva->ptr);
free(tva);
}
tnode *tnode_init() {
tnode *t;
t = calloc(1, sizeof(*t));
assert(t);
return t;
}
void tnode_free(tnode *t ) {
if (IS_STRING(t)) {
buffer_free(t->value.data.str);
}
if (t->r) tnode_free(t->r);
if (t->l) tnode_free(t->l);
free(t);
}
int tnode_prepare_long(tnode *t) {
switch (t->value.type) {
case T_NODE_VALUE_STRING:
buffer_free(t->value.data.str);
break;
default:
break;
}
t->value.type = T_NODE_VALUE_LONG;
return 0;
}
int tnode_prepare_string(tnode *t) {
switch (t->value.type) {
case T_NODE_VALUE_STRING:
buffer_reset(t->value.data.str);
break;
default:
t->value.data.str = buffer_init();
break;
}
t->value.type = T_NODE_VALUE_STRING;
return 0;
}
int cache_trigger_parse(server *srv, connection *con, plugin_data *p, buffer *t /* trigger */, tnode *n) {
size_t i, pre = 0, post = 0;
int braces = 0;
int quotes = 0;
/*
* unix.time.now - file.mtime("head.html") > 30
*/
cache_trigger_functions f[] = {
{ "file.mtime", 1, f_file_mtime },
{ "unix.time.now", 0, f_unix_time_now },
{ "mysql.escape", 1, f_mysql_escape },
{ "mysql.connect", 4, f_mysql_connect },
{ "mysql.query", 1, f_mysql_query },
{ NULL, 0, NULL },
};
if (t->used == 0) {
log_error_write(srv, __FILE__, __LINE__, "s", "empty term");
return -1;
}
n->op = UNSET;
/* search for the highest op */
for (i = 0; i < t->used; i++) {
switch(t->ptr[i]) {
case '\\':
switch (t->ptr[i+1]) {
case '"': if (quotes) i++; break;
}
break;
case '"':
quotes = quotes ? 0 : 1;
break;
}
if (quotes) continue;
switch(t->ptr[i]) {
case '(': braces++; break;
case ')': braces--;
if (braces < 0) {
log_error_write(srv, __FILE__, __LINE__, "ss", "braces don't match:", t->ptr);
return -1;
}
break;
}
if (braces) continue;
switch(t->ptr[i]) {
case '-': if (MINUS >= n->op) { n->op = MINUS; pre = i - 1; post = i + 1; } break;
case '+': if (PLUS >= n->op) { n->op = PLUS; pre = i - 1; post = i + 1; } break;
case '*': if (TIMES >= n->op) { n->op = TIMES; pre = i - 1; post = i + 1; } break;
case '/': if (PART >= n->op) { n->op = PART; pre = i - 1; post = i + 1; } break;
case '>':
switch(t->ptr[i+1]) {
case '=':
if (GE > n->op) { n->op = GE; pre = i - 1; post = i + 2; }
i++;
break;
default:
if (GT > n->op) { n->op = GT; pre = i - 1; post = i + 1; }
break;
}
break;
case '<':
switch(t->ptr[i+1]) {
case '=':
if (LE > n->op) { n->op = LE; pre = i - 1; post = i + 2; }
i++;
break;
default:
if (LT > n->op) { n->op = LT; pre = i - 1; post = i + 1; }
break;
}
break;
case '!':
switch(t->ptr[i+1]) {
case '=':
if (NE > n->op) { n->op = NE; pre = i - 1; post = i + 2; }
i++;
break;
}
break;
case '&':
switch(t->ptr[i+1]) {
case '&':
if (AND > n->op) { n->op = AND; pre = i - 1; post = i + 2; }
i++;
break;
}
break;
case '|':
switch(t->ptr[i+1]) {
case '|':
if (OR > n->op) { n->op = OR; pre = i - 1; post = i + 2; }
i++;
break;
}
break;
case '=': if (EQ > n->op) { n->op = EQ; pre = i - 1; post = i + 1; } break;
}
}
if (braces != 0) {
log_error_write(srv, __FILE__, __LINE__, "ss", "braces don't match:", t->ptr);
return -1;
}
if (quotes != 0) {
log_error_write(srv, __FILE__, __LINE__, "ss", "quotes don't match:", t->ptr);
return -1;
}
if (n->op != UNSET) {
buffer *b;
b = buffer_init();
/* strip spaces */
for (i = pre; t->ptr[i] == ' ' || t->ptr[i] == '\t'; i--);
buffer_copy_string_len(b, t->ptr, i + 1);
if (n->l == NULL) n->l = tnode_init();
if (-1 == cache_trigger_parse(srv, con, p, b, n->l)) {
buffer_free(b);
return -1;
}
for (i = post; t->ptr[i] == ' ' || t->ptr[i] == '\t'; i++);
buffer_copy_string_len(b, t->ptr + i, t->used - i - 1);
if (n->r == NULL) n->r = tnode_init();
if (-1 == cache_trigger_parse(srv, con, p, b, n->r)) {
buffer_free(b);
return -1;
}
buffer_free(b);
} else {
char *br_open;
if (t->ptr[0] == '(' &&
t->ptr[t->used - 2] == ')') {
buffer *b;
b = buffer_init();
buffer_copy_string_len(b, t->ptr + 1, t->used - 3);
if (-1 == cache_trigger_parse(srv, con, p, b, n)) {
buffer_free(b);
return -1;
}
buffer_free(b);
return 0;
} else if (t->ptr[0] == '"' &&
t->ptr[t->used - 2] == '"') {
/* a string */
tnode_prepare_string(n);
buffer_copy_string_len(n->value.data.str, t->ptr + 1, t->used - 3);
return 0;
} else if (NULL != (br_open = strchr(t->ptr, '(')) &&
t->ptr[t->used - 2] == ')') {
/* no basic op, perhaps a function */
for (i = 0; f[i].name; i++) {
size_t slen;
slen = br_open - t->ptr;
if ((strlen(f[i].name) == slen) &&
(0 == strncmp(f[i].name, t->ptr, slen))) {
/* we know the function */
/* parse parameters */
if (0 != cache_parse_parameters(srv, con, p, br_open + 1, t->used - slen - 3, p->params)) {
fprintf(stderr, "%s.%d: parsing parameters failed\n",
__FILE__, __LINE__
);
return -1;
}
if (p->params->used != f[i].params) {
fprintf(stderr, "%s.%d: wrong param-count for %s: %d, expected %d\n",
__FILE__, __LINE__,
f[i].name,
p->params->used,
f[i].params
);
return -1;
}
if (0 != f[i].func(srv, con, p, n)) {
fprintf(stderr, "%s.%d: function %s failed\n", __FILE__, __LINE__, f[i].name);
return -1;
}
return 0;
}
}
} else {
char *err;
tnode_prepare_long(n);
VAL_LONG(n) = strtol(t->ptr, &err, 10);
if (*err != '\0') {
/* isn't a int */
fprintf(stderr, "%s.%d: can't evaluate: '%s', '%s'\n",
__FILE__, __LINE__,
t->ptr, err);
return -1;
}
}
}
return 0;
}
int cache_ops_long(tnode *res, tnode *l, tnode *r) {
if (!IS_LONG(l) || !IS_LONG(r)) {
return -1;
}
#define OP1(x, y) \
case x: \
VAL_LONG(res) = (VAL_LONG(l) y VAL_LONG(r)); \
break;
#define OP2(x, y) \
case x: \
VAL_LONG(res) = (VAL_LONG(l) y VAL_LONG(r)); \
break;
switch(res->op) {
OP1(MINUS, -);
OP1(PLUS, +);
OP1(TIMES, *);
OP1(PART, /);
OP2(GT, >);
OP2(LT, <);
OP2(EQ, ==);
OP2(NE, !=);
OP2(LE, >=);
OP2(GE, <=);
OP2(AND, &&);
OP2(OR, ||);
default:
return -1;
}
#undef OP1
#undef OP2
tnode_prepare_long(res);
res->value.type = T_NODE_VALUE_LONG;
res->op = UNSET;
return 0;
}
int cache_ops_string(tnode *res, tnode *l, tnode *r) {
if (!IS_STRING(l) || !IS_STRING(r)) {
return -1;
}
switch(res->op) {
case PLUS:
tnode_prepare_string(res);
buffer_copy_string_buffer(VAL_STRING(res), VAL_STRING(l));
buffer_append_string_buffer(VAL_STRING(res), VAL_STRING(r));
res->value.type = T_NODE_VALUE_STRING;
res->op = UNSET;
break;
default:
return -1;
}
return 0;
}
int cache_trigger_eval(server *srv, connection *con, plugin_data *p, tnode *t) {
if (t->op == UNSET) {
/* a value */
return 0;
}
if (t->l->op != UNSET) {
if (-1 == cache_trigger_eval(srv, con, p, t->l)) {
fprintf(stderr, "%s.%d\n", __FILE__, __LINE__);
return -1;
}
}
if (t->r->op != UNSET) {
if (-1 == cache_trigger_eval(srv, con, p, t->r)) {
fprintf(stderr, "%s.%d\n", __FILE__, __LINE__);
return -1;
}
}
/* left and right are simple datatypes now */
if (IS_LONG(t->l) && IS_LONG(t->r)) {
if (-1 == cache_ops_long(t, t->l, t->r)) {
fprintf(stderr, "%s.%d\n", __FILE__, __LINE__);
return -1;
}
} else if (IS_STRING(t->l) && IS_STRING(t->r)) {
if (-1 == cache_ops_string(t, t->l, t->r)) {
fprintf(stderr, "%s.%d: cache_ops_string failed\n", __FILE__, __LINE__);
return -1;
}
} else {
fprintf(stderr, "%s.%d: typemismatch\n",
__FILE__, __LINE__);
return -1;
}
return 0;
}
int cache_trigger(server *srv, connection *con, plugin_data *p) {
size_t i;
tnode *t;
t = tnode_init();
for (i = 0; i < p->trigger_if->used; i++) {
if (-1 == cache_trigger_parse(srv, con, p, p->trigger_if->ptr[i], t)) {
fprintf(stderr, "%s.%d: cache_trigger_parse failed\n", __FILE__, __LINE__);
return -1;
}
if (-1 == cache_trigger_eval(srv, con, p, t)) {
fprintf(stderr, "%s.%d: cache_trigger_eval failed\n", __FILE__, __LINE__);
return -1;
}
if (IS_LONG(t)) {
#if 0
fprintf(stderr, "eval: %s = %ld\n", p->trigger_if->ptr[i]->ptr, VAL_LONG(t));
#endif
if (VAL_LONG(t) != 0) {
tnode_free(t);
return 1;
}
} else if (IS_STRING(t)) {
#if 0
fprintf(stderr, "eval: %s = '%s'\n", p->trigger_if->ptr[i]->ptr, VAL_STRING(t)->ptr);
#endif
if (VAL_STRING(t)->used > 1) {
tnode_free(t);
return 1;
}
}
}
tnode_free(t);
return 0;
}
/**
* parse the cache-file
*
* if cache-file is broken, call handler
* if no handler is set, report 500
*
* known keywords
*
* - include
* - content-type
*
*/
int cache_parse(server *srv, connection *con, plugin_data *p, buffer *fn) {
stream cf;
char *line, *end;
int r;
if (0 != stream_open(&cf, fn)) {
log_error_write(srv, __FILE__, __LINE__, "s", strerror(errno));
return -1;
}
buffer_reset(srv->tmp_buf);
for (line = cf.start;
NULL != (end = (memchr(line, '\n', cf.size - (line - cf.start)))) && (end - cf.start < cf.size);
line = end + 1) {
int s_len, key_len;
char *value;
s_len = end - line;
if (*line == '#') continue;
if (s_len == 0) continue;
if (line[s_len-1] == '\\') {
/* backslash at the end of the line */
buffer_append_string_len(srv->tmp_buf, line, s_len - 1);
continue;
}
buffer_append_string_len(srv->tmp_buf, line, s_len);
if ((NULL == (value = strchr(srv->tmp_buf->ptr, ' '))) &&
(NULL == (value = strchr(srv->tmp_buf->ptr, '\t')))) {
log_error_write(srv, __FILE__, __LINE__, "sss", fn->ptr, "whitespace is missing: <key> <value>", srv->tmp_buf->ptr);
stream_close(&cf);
return -1;
}
key_len = value - srv->tmp_buf->ptr;
/* strip spaces */
for(; *value == ' '; value++);
switch(key_len) {
case 4:
if (0 == strncmp(srv->tmp_buf->ptr, "eval", key_len)) {
buffer *b;
b = buffer_array_append_get_buffer(p->eval);
buffer_copy_string(b, value);
} else {
log_error_write(srv, __FILE__, __LINE__, "db", key_len, srv->tmp_buf);
}
break;
case 10:
if (0 == strncmp(srv->tmp_buf->ptr, "trigger.if", key_len)) {
buffer *b;
b = buffer_array_append_get_buffer(p->trigger_if);
buffer_copy_string(b, value);
} else {
log_error_write(srv, __FILE__, __LINE__, "db", key_len, srv->tmp_buf);
}
break;
case 14:
if (0 == strncmp(srv->tmp_buf->ptr, "output.include", key_len)) {
struct stat st;
buffer *b;
b = buffer_array_append_get_buffer(p->output_include);
buffer_copy_string_buffer(b, p->basedir);
buffer_append_string(b, value);
if (-1 == stat(b->ptr, &st)) {
log_error_write(srv, __FILE__, __LINE__, "sbs", "output.include:", b, strerror(errno));
p->output_include->used--;
stream_close(&cf);
return -1;
}
} else {
log_error_write(srv, __FILE__, __LINE__, "db", key_len, srv->tmp_buf);
}
break;
case 15:
if (0 == strncmp(srv->tmp_buf->ptr, "trigger.handler", key_len)) {
/*
* if one of the trigger.if's is true, the trigger.handler is called
*
* the output of the trigger is sent to the client
*
*/
buffer *b;
b = p->trigger_handler;
buffer_copy_string_buffer(b, p->basedir);
buffer_append_string(b, value);
} else {
log_error_write(srv, __FILE__, __LINE__, "db", key_len, srv->tmp_buf);
}
break;
case 19:
if (0 == strncmp(srv->tmp_buf->ptr, "output.content-type", key_len)) {
response_header_insert(srv, con, CONST_STR_LEN("Content-Type"), value, strlen(value));
} else {
log_error_write(srv, __FILE__, __LINE__, "db", key_len, srv->tmp_buf);
}
break;
default:
log_error_write(srv, __FILE__, __LINE__, "db", key_len, srv->tmp_buf);
break;
}
buffer_reset(srv->tmp_buf);
}
stream_close(&cf);
if (p->trigger_handler->used && (0 != (r = cache_trigger(srv, con, p)))) {
if (r == -1) return -1;
/* triggering */
/* rewrite filename */
buffer_copy_string_buffer(con->physical.path, p->trigger_handler);
chunkqueue_reset(con->write_queue);
return 1;
} else {
size_t i;
struct stat st;
for (i = 0; i < p->output_include->used; i++) {
buffer *b = p->output_include->ptr[i];
stat(b->ptr, &st);
chunkqueue_append_file(con->write_queue, b, 0, st.st_size);
}
con->file_finished = 1;
}
return 0;
}
int cache_parse_parameter(server *srv, connection *con, plugin_data *p, const char *param, size_t param_len, tnode_val *val) {
buffer *b;
tnode *t;
b = buffer_init();
buffer_copy_string_len(b, param, param_len);
t = tnode_init();
/* we got an expression */
if (-1 == cache_trigger_parse(srv, con, p, b, t)) {
fprintf(stderr, "%s.%d: cache_trigger_parse failed: %s\n", __FILE__, __LINE__, b->ptr);
return -1;
}
if (-1 == cache_trigger_eval(srv, con, p, t)) {
fprintf(stderr, "%s.%d: cache_trigger_eval failed\n", __FILE__, __LINE__);
return -1;
}
buffer_free(b);
/* t is our value */
tnode_val_move(val, &(t->value));
tnode_free(t);
return 0;
}
int cache_parse_parameters(server *srv, connection *con, plugin_data *p, const char *params, size_t param_len, tnode_val_array *res) {
/*
* scheme:
* <expression>[, <expression>]
*
*/
size_t i;
int quotes = 0;
const char *start;
tnode_val *tv;
start = params;
for (i = 0; i < param_len; i++) {
char c = params[i];
if (c == '\\' && params[i+1] == '"' && quotes) i++;
if (c == '"') quotes = quotes ? 0 : 1;
if (!quotes) {
if (c == ',') {
tv = tnode_val_init();
if (-1 == cache_parse_parameter(srv, con, p, start, params + (i-1) - start, tv)) {
fprintf(stderr, "%s.%d: cache_parse_parameter failed: %s\n", __FILE__, __LINE__, start);
return -1;
}
tnode_val_array_append(res, tv);
start = params + i + 1;
}
}
}
tv = tnode_val_init();
if (-1 == cache_parse_parameter(srv, con, p, start, params + param_len - start, tv)) {
fprintf(stderr, "%s.%d: cache_parse_parameter failed: %s\n", __FILE__, __LINE__, start);
return -1;
}
tnode_val_array_append(res, tv);
if (quotes != 0) {
fprintf(stderr, "%s.%d: quotes don't match: %s\n", __FILE__, __LINE__, params);
return -1;
}
return 0;
}
Loading…
Cancel
Save