[mod_ssi] merge mod_ssi_expr.c into mod_ssi.c
isolate this ancient relicpersonal/stbuehler/tests-path
parent
457362cbe3
commit
741513ecd1
|
@ -838,7 +838,7 @@ add_and_install_library(mod_secdownload "mod_secdownload.c;algo_hmac.c")
|
|||
add_and_install_library(mod_setenv mod_setenv.c)
|
||||
add_and_install_library(mod_simple_vhost mod_simple_vhost.c)
|
||||
add_and_install_library(mod_sockproxy mod_sockproxy.c)
|
||||
add_and_install_library(mod_ssi "mod_ssi_expr.c;mod_ssi.c")
|
||||
add_and_install_library(mod_ssi mod_ssi.c)
|
||||
add_and_install_library(mod_staticfile mod_staticfile.c)
|
||||
add_and_install_library(mod_status mod_status.c)
|
||||
add_and_install_library(mod_uploadprogress mod_uploadprogress.c)
|
||||
|
|
|
@ -282,7 +282,7 @@ mod_sockproxy_la_LDFLAGS = $(common_module_ldflags)
|
|||
mod_sockproxy_la_LIBADD = $(common_libadd)
|
||||
|
||||
lib_LTLIBRARIES += mod_ssi.la
|
||||
mod_ssi_la_SOURCES = mod_ssi_expr.c mod_ssi.c
|
||||
mod_ssi_la_SOURCES = mod_ssi.c
|
||||
mod_ssi_la_LDFLAGS = $(common_module_ldflags)
|
||||
mod_ssi_la_LIBADD = $(common_libadd)
|
||||
|
||||
|
@ -478,7 +478,6 @@ hdr = base64.h buffer.h burl.h network.h log.h http_kv.h keyvalue.h \
|
|||
plugin.h plugin_config.h \
|
||||
http_etag.h array.h vector.h \
|
||||
fdevent_impl.h network_write.h configfile.h \
|
||||
mod_ssi.h \
|
||||
sock_addr_cache.h \
|
||||
configparser.h \
|
||||
rand.h \
|
||||
|
@ -528,7 +527,7 @@ lighttpd_SOURCES = \
|
|||
mod_secdownload.c algo_hmac.c \
|
||||
mod_setenv.c \
|
||||
mod_simple_vhost.c \
|
||||
mod_ssi_expr.c mod_ssi.c \
|
||||
mod_ssi.c \
|
||||
mod_staticfile.c \
|
||||
mod_status.c \
|
||||
mod_uploadprogress.c \
|
||||
|
|
|
@ -119,7 +119,7 @@ modules = {
|
|||
'mod_setenv' : { 'src' : [ 'mod_setenv.c' ] },
|
||||
'mod_simple_vhost' : { 'src' : [ 'mod_simple_vhost.c' ] },
|
||||
'mod_sockproxy' : { 'src' : [ 'mod_sockproxy.c' ] },
|
||||
'mod_ssi' : { 'src' : [ 'mod_ssi_expr.c', 'mod_ssi.c' ] },
|
||||
'mod_ssi' : { 'src' : [ 'mod_ssi.c' ] },
|
||||
'mod_staticfile' : { 'src' : [ 'mod_staticfile.c' ] },
|
||||
'mod_status' : { 'src' : [ 'mod_status.c' ] },
|
||||
'mod_uploadprogress' : { 'src' : [ 'mod_uploadprogress.c' ] },
|
||||
|
|
|
@ -1117,7 +1117,7 @@ modules = [
|
|||
[ 'mod_setenv', [ 'mod_setenv.c' ] ],
|
||||
[ 'mod_simple_vhost', [ 'mod_simple_vhost.c' ] ],
|
||||
[ 'mod_sockproxy', [ 'mod_sockproxy.c' ] ],
|
||||
[ 'mod_ssi', [ 'mod_ssi_expr.c', 'mod_ssi.c' ], libws2_32 ],
|
||||
[ 'mod_ssi', [ 'mod_ssi.c' ], libws2_32 ],
|
||||
[ 'mod_staticfile', [ 'mod_staticfile.c' ] ],
|
||||
[ 'mod_status', [ 'mod_status.c' ] ],
|
||||
[ 'mod_uploadprogress', [ 'mod_uploadprogress.c' ] ],
|
||||
|
|
374
src/mod_ssi.c
374
src/mod_ssi.c
|
@ -3,7 +3,9 @@
|
|||
#include "fdevent.h"
|
||||
#include "fdlog.h"
|
||||
#include "log.h"
|
||||
#include "array.h"
|
||||
#include "buffer.h"
|
||||
#include "chunk.h"
|
||||
#include "http_cgi.h"
|
||||
#include "http_chunk.h"
|
||||
#include "http_etag.h"
|
||||
|
@ -15,8 +17,6 @@
|
|||
|
||||
#include "response.h"
|
||||
|
||||
#include "mod_ssi.h"
|
||||
|
||||
#include "sys-socket.h"
|
||||
#include "sys-time.h"
|
||||
|
||||
|
@ -41,6 +41,39 @@
|
|||
# include <sys/filio.h>
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const array *ssi_extension;
|
||||
const buffer *content_type;
|
||||
unsigned short conditional_requests;
|
||||
unsigned short ssi_exec;
|
||||
unsigned short ssi_recursion_max;
|
||||
} plugin_config;
|
||||
|
||||
typedef struct {
|
||||
PLUGIN_DATA;
|
||||
plugin_config defaults;
|
||||
plugin_config conf;
|
||||
array *ssi_vars;
|
||||
array *ssi_cgi_env;
|
||||
buffer stat_fn;
|
||||
buffer timefmt;
|
||||
} plugin_data;
|
||||
|
||||
typedef struct {
|
||||
array *ssi_vars;
|
||||
array *ssi_cgi_env;
|
||||
buffer *stat_fn;
|
||||
buffer *timefmt;
|
||||
int sizefmt;
|
||||
|
||||
int if_level, if_is_false_level, if_is_false, if_is_false_endif;
|
||||
unsigned short ssi_recursion_depth;
|
||||
|
||||
chunkqueue wq;
|
||||
log_error_st *errh;
|
||||
plugin_config conf;
|
||||
} handler_ctx;
|
||||
|
||||
static handler_ctx * handler_ctx_init(plugin_data *p, log_error_st *errh) {
|
||||
handler_ctx *hctx = calloc(1, sizeof(*hctx));
|
||||
force_assert(hctx);
|
||||
|
@ -178,6 +211,343 @@ SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#define TK_AND 1
|
||||
#define TK_OR 2
|
||||
#define TK_EQ 3
|
||||
#define TK_NE 4
|
||||
#define TK_GT 5
|
||||
#define TK_GE 6
|
||||
#define TK_LT 7
|
||||
#define TK_LE 8
|
||||
#define TK_NOT 9
|
||||
#define TK_LPARAN 10
|
||||
#define TK_RPARAN 11
|
||||
#define TK_VALUE 12
|
||||
|
||||
typedef struct {
|
||||
const char *input;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
int in_brace;
|
||||
int depth;
|
||||
handler_ctx *p;
|
||||
} ssi_tokenizer_t;
|
||||
|
||||
typedef struct {
|
||||
buffer str;
|
||||
enum { SSI_TYPE_UNSET, SSI_TYPE_BOOL, SSI_TYPE_STRING } type;
|
||||
int bo;
|
||||
} ssi_val_t;
|
||||
|
||||
__attribute_pure__
|
||||
static int ssi_val_tobool(const ssi_val_t *B) {
|
||||
return B->type == SSI_TYPE_BOOL ? B->bo : !buffer_is_blank(&B->str);
|
||||
}
|
||||
|
||||
__attribute_pure__
|
||||
static int ssi_eval_expr_cmp(const ssi_val_t * const v1, const ssi_val_t * const v2, const int cond) {
|
||||
int cmp = (v1->type != SSI_TYPE_BOOL && v2->type != SSI_TYPE_BOOL)
|
||||
? strcmp(v1->str.ptr ? v1->str.ptr : "",
|
||||
v2->str.ptr ? v2->str.ptr : "")
|
||||
: ssi_val_tobool(v1) - ssi_val_tobool(v2);
|
||||
switch (cond) {
|
||||
case TK_EQ: return (cmp == 0);
|
||||
case TK_NE: return (cmp != 0);
|
||||
case TK_GE: return (cmp >= 0);
|
||||
case TK_GT: return (cmp > 0);
|
||||
case TK_LE: return (cmp <= 0);
|
||||
case TK_LT: return (cmp < 0);
|
||||
default: return 0;/*(should not happen)*/
|
||||
}
|
||||
}
|
||||
|
||||
__attribute_pure__
|
||||
static int ssi_eval_expr_cmp_bool(const ssi_val_t * const v1, const ssi_val_t * const v2, const int cond) {
|
||||
return (cond == TK_OR)
|
||||
? ssi_val_tobool(v1) || ssi_val_tobool(v2) /* TK_OR */
|
||||
: ssi_val_tobool(v1) && ssi_val_tobool(v2); /* TK_AND */
|
||||
}
|
||||
|
||||
static void ssi_eval_expr_append_val(buffer * const b, const char *s, const size_t slen) {
|
||||
if (buffer_is_blank(b))
|
||||
buffer_append_string_len(b, s, slen);
|
||||
else if (slen)
|
||||
buffer_append_str2(b, CONST_STR_LEN(" "), s, slen);
|
||||
}
|
||||
|
||||
static int ssi_expr_tokenizer(ssi_tokenizer_t * const t, buffer * const token) {
|
||||
size_t i;
|
||||
|
||||
while (t->offset < t->size
|
||||
&& (t->input[t->offset] == ' ' || t->input[t->offset] == '\t')) {
|
||||
++t->offset;
|
||||
}
|
||||
if (t->offset >= t->size)
|
||||
return 0;
|
||||
if (t->input[t->offset] == '\0') {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu foobar", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (t->input[t->offset]) {
|
||||
case '=':
|
||||
#if 0 /*(maybe accept "==", too)*/
|
||||
if (t->input[t->offset + 1] == '=')
|
||||
++t->offset;
|
||||
#endif
|
||||
t->offset++;
|
||||
return TK_EQ;
|
||||
case '>':
|
||||
if (t->input[t->offset + 1] == '=') {
|
||||
t->offset += 2;
|
||||
return TK_GE;
|
||||
}
|
||||
else {
|
||||
t->offset += 1;
|
||||
return TK_GT;
|
||||
}
|
||||
case '<':
|
||||
if (t->input[t->offset + 1] == '=') {
|
||||
t->offset += 2;
|
||||
return TK_LE;
|
||||
}
|
||||
else {
|
||||
t->offset += 1;
|
||||
return TK_LT;
|
||||
}
|
||||
case '!':
|
||||
if (t->input[t->offset + 1] == '=') {
|
||||
t->offset += 2;
|
||||
return TK_NE;
|
||||
}
|
||||
else {
|
||||
t->offset += 1;
|
||||
return TK_NOT;
|
||||
}
|
||||
case '&':
|
||||
if (t->input[t->offset + 1] == '&') {
|
||||
t->offset += 2;
|
||||
return TK_AND;
|
||||
}
|
||||
else {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing second &", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
case '|':
|
||||
if (t->input[t->offset + 1] == '|') {
|
||||
t->offset += 2;
|
||||
return TK_OR;
|
||||
}
|
||||
else {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing second |", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
case '(':
|
||||
t->offset++;
|
||||
t->in_brace++;
|
||||
return TK_LPARAN;
|
||||
case ')':
|
||||
t->offset++;
|
||||
t->in_brace--;
|
||||
return TK_RPARAN;
|
||||
case '\'':
|
||||
/* search for the terminating "'" */
|
||||
i = 1;
|
||||
while (t->input[t->offset + i] && t->input[t->offset + i] != '\'')
|
||||
++i;
|
||||
if (t->input[t->offset + i]) {
|
||||
ssi_eval_expr_append_val(token, t->input + t->offset + 1, i-1);
|
||||
t->offset += i + 1;
|
||||
return TK_VALUE;
|
||||
}
|
||||
else {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing closing quote", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
case '$': {
|
||||
const char *var;
|
||||
size_t varlen;
|
||||
if (t->input[t->offset + 1] == '{') {
|
||||
i = 2;
|
||||
while (t->input[t->offset + i] && t->input[t->offset + i] != '}')
|
||||
++i;
|
||||
if (t->input[t->offset + i] != '}') {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing closing curly-brace", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
++i; /* step past '}' */
|
||||
var = t->input + t->offset + 2;
|
||||
varlen = i-3;
|
||||
}
|
||||
else {
|
||||
for (i = 1; light_isalpha(t->input[t->offset + i]) ||
|
||||
t->input[t->offset + i] == '_' ||
|
||||
((i > 1) && light_isdigit(t->input[t->offset + i])); ++i) ;
|
||||
var = t->input + t->offset + 1;
|
||||
varlen = i-1;
|
||||
}
|
||||
|
||||
const data_string *ds;
|
||||
if ((ds = (const data_string *)
|
||||
array_get_element_klen(t->p->ssi_cgi_env, var, varlen))
|
||||
|| (ds = (const data_string *)
|
||||
array_get_element_klen(t->p->ssi_vars, var, varlen)))
|
||||
ssi_eval_expr_append_val(token, BUF_PTR_LEN(&ds->value));
|
||||
t->offset += i;
|
||||
return TK_VALUE;
|
||||
}
|
||||
default:
|
||||
for (i = 0; isgraph(((unsigned char *)t->input)[t->offset + i]); ++i) {
|
||||
char d = t->input[t->offset + i];
|
||||
switch(d) {
|
||||
default: continue;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case ')':
|
||||
case '(':
|
||||
case '\'':
|
||||
case '=':
|
||||
case '!':
|
||||
case '<':
|
||||
case '>':
|
||||
case '&':
|
||||
case '|':
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ssi_eval_expr_append_val(token, t->input + t->offset, i);
|
||||
t->offset += i;
|
||||
return TK_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
static int ssi_eval_expr_loop(ssi_tokenizer_t * const t, ssi_val_t * const v);
|
||||
|
||||
static int ssi_eval_expr_step(ssi_tokenizer_t * const t, ssi_val_t * const v) {
|
||||
buffer_clear(&v->str);
|
||||
v->type = SSI_TYPE_UNSET; /*(not SSI_TYPE_BOOL)*/
|
||||
int next;
|
||||
const int level = t->in_brace;
|
||||
switch ((next = ssi_expr_tokenizer(t, &v->str))) {
|
||||
case TK_VALUE:
|
||||
do { next = ssi_expr_tokenizer(t, &v->str); } while (next == TK_VALUE);
|
||||
return next;
|
||||
case TK_LPARAN:
|
||||
if (t->in_brace > 16) return -1; /*(arbitrary limit)*/
|
||||
next = ssi_eval_expr_loop(t, v);
|
||||
if (next == TK_RPARAN && level == t->in_brace) {
|
||||
int result = ssi_val_tobool(v);
|
||||
next = ssi_eval_expr_step(t, v); /*(resets v)*/
|
||||
v->bo = result;
|
||||
v->type = SSI_TYPE_BOOL;
|
||||
return (next==TK_AND || next==TK_OR || next==TK_RPARAN || 0==next)
|
||||
? next
|
||||
: -1;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
case TK_RPARAN:
|
||||
return t->in_brace >= 0 ? TK_RPARAN : -1;
|
||||
case TK_NOT:
|
||||
if (++t->depth > 16) return -1; /*(arbitrary limit)*/
|
||||
next = ssi_eval_expr_step(t, v);
|
||||
--t->depth;
|
||||
if (-1 == next) return next;
|
||||
v->bo = !ssi_val_tobool(v);
|
||||
v->type = SSI_TYPE_BOOL;
|
||||
return next;
|
||||
default:
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
static int ssi_eval_expr_loop_cmp(ssi_tokenizer_t * const t, ssi_val_t * const v1, int cond) {
|
||||
ssi_val_t v2 = { { NULL, 0, 0 }, SSI_TYPE_UNSET, 0 };
|
||||
int next = ssi_eval_expr_step(t, &v2);
|
||||
if (-1 != next) {
|
||||
v1->bo = ssi_eval_expr_cmp(v1, &v2, cond);
|
||||
v1->type = SSI_TYPE_BOOL;
|
||||
}
|
||||
buffer_free_ptr(&v2.str);
|
||||
return next;
|
||||
}
|
||||
|
||||
static int ssi_eval_expr_loop(ssi_tokenizer_t * const t, ssi_val_t * const v1) {
|
||||
int next = ssi_eval_expr_step(t, v1);
|
||||
switch (next) {
|
||||
case TK_AND: case TK_OR:
|
||||
break;
|
||||
case TK_EQ: case TK_NE:
|
||||
case TK_GT: case TK_GE:
|
||||
case TK_LT: case TK_LE:
|
||||
next = ssi_eval_expr_loop_cmp(t, v1, next);
|
||||
if (next == TK_RPARAN || 0 == next) return next;
|
||||
if (next != TK_AND && next != TK_OR) {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu parser failed somehow near here", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return next;
|
||||
}
|
||||
|
||||
/*(Note: '&&' and '||' evaluations are not short-circuited)*/
|
||||
ssi_val_t v2 = { { NULL, 0, 0 }, SSI_TYPE_UNSET, 0 };
|
||||
do {
|
||||
int cond = next;
|
||||
next = ssi_eval_expr_step(t, &v2);
|
||||
switch (next) {
|
||||
case TK_AND: case TK_OR: case 0:
|
||||
break;
|
||||
case TK_EQ: case TK_NE:
|
||||
case TK_GT: case TK_GE:
|
||||
case TK_LT: case TK_LE:
|
||||
next = ssi_eval_expr_loop_cmp(t, &v2, next);
|
||||
if (-1 == next) continue;
|
||||
break;
|
||||
case TK_RPARAN:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
v1->bo = ssi_eval_expr_cmp_bool(v1, &v2, cond);
|
||||
v1->type = SSI_TYPE_BOOL;
|
||||
} while (next == TK_AND || next == TK_OR);
|
||||
buffer_free_ptr(&v2.str);
|
||||
return next;
|
||||
}
|
||||
|
||||
static int ssi_eval_expr(handler_ctx *p, const char *expr) {
|
||||
ssi_tokenizer_t t;
|
||||
t.input = expr;
|
||||
t.offset = 0;
|
||||
t.size = strlen(expr);
|
||||
t.in_brace = 0;
|
||||
t.depth = 0;
|
||||
t.p = p;
|
||||
|
||||
ssi_val_t v = { { NULL, 0, 0 }, SSI_TYPE_UNSET, 0 };
|
||||
int rc = ssi_eval_expr_loop(&t, &v);
|
||||
rc = (0 == rc && 0 == t.in_brace && 0 == t.depth)
|
||||
? ssi_val_tobool(&v)
|
||||
: -1;
|
||||
buffer_free_ptr(&v.str);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int ssi_env_add(void *venv, const char *key, size_t klen, const char *val, size_t vlen) {
|
||||
array_set_key_value((array *)venv, key, klen, val, vlen);
|
||||
return 0;
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef _MOD_SSI_H_
|
||||
#define _MOD_SSI_H_
|
||||
#include "first.h"
|
||||
|
||||
#include "base_decls.h"
|
||||
#include "buffer.h"
|
||||
#include "array.h"
|
||||
#include "chunk.h"
|
||||
|
||||
#include "plugin.h"
|
||||
|
||||
typedef struct {
|
||||
const array *ssi_extension;
|
||||
const buffer *content_type;
|
||||
unsigned short conditional_requests;
|
||||
unsigned short ssi_exec;
|
||||
unsigned short ssi_recursion_max;
|
||||
} plugin_config;
|
||||
|
||||
typedef struct {
|
||||
PLUGIN_DATA;
|
||||
plugin_config defaults;
|
||||
plugin_config conf;
|
||||
array *ssi_vars;
|
||||
array *ssi_cgi_env;
|
||||
buffer stat_fn;
|
||||
buffer timefmt;
|
||||
} plugin_data;
|
||||
|
||||
typedef struct {
|
||||
array *ssi_vars;
|
||||
array *ssi_cgi_env;
|
||||
buffer *stat_fn;
|
||||
buffer *timefmt;
|
||||
int sizefmt;
|
||||
|
||||
int if_level, if_is_false_level, if_is_false, if_is_false_endif;
|
||||
unsigned short ssi_recursion_depth;
|
||||
|
||||
chunkqueue wq;
|
||||
log_error_st *errh;
|
||||
plugin_config conf;
|
||||
} handler_ctx;
|
||||
|
||||
int ssi_eval_expr(handler_ctx *p, const char *expr);
|
||||
|
||||
#endif
|
|
@ -1,335 +0,0 @@
|
|||
#include "first.h"
|
||||
|
||||
#include "buffer.h"
|
||||
#include "log.h"
|
||||
#include "mod_ssi.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TK_AND 1
|
||||
#define TK_OR 2
|
||||
#define TK_EQ 3
|
||||
#define TK_NE 4
|
||||
#define TK_GT 5
|
||||
#define TK_GE 6
|
||||
#define TK_LT 7
|
||||
#define TK_LE 8
|
||||
#define TK_NOT 9
|
||||
#define TK_LPARAN 10
|
||||
#define TK_RPARAN 11
|
||||
#define TK_VALUE 12
|
||||
|
||||
typedef struct {
|
||||
const char *input;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
|
||||
int in_brace;
|
||||
int depth;
|
||||
handler_ctx *p;
|
||||
} ssi_tokenizer_t;
|
||||
|
||||
typedef struct {
|
||||
enum { SSI_TYPE_UNSET, SSI_TYPE_BOOL, SSI_TYPE_STRING } type;
|
||||
|
||||
buffer *str;
|
||||
int bo;
|
||||
} ssi_val_t;
|
||||
|
||||
__attribute_pure__
|
||||
static int ssi_val_tobool(const ssi_val_t *B) {
|
||||
return B->type == SSI_TYPE_BOOL ? B->bo : !buffer_is_blank(B->str);
|
||||
}
|
||||
|
||||
__attribute_pure__
|
||||
static int ssi_eval_expr_cmp(const ssi_val_t * const v1, const ssi_val_t * const v2, const int cond) {
|
||||
int cmp = (v1->type != SSI_TYPE_BOOL && v2->type != SSI_TYPE_BOOL)
|
||||
? strcmp(v1->str->ptr ? v1->str->ptr : "",
|
||||
v2->str->ptr ? v2->str->ptr : "")
|
||||
: ssi_val_tobool(v1) - ssi_val_tobool(v2);
|
||||
switch (cond) {
|
||||
case TK_EQ: return (cmp == 0);
|
||||
case TK_NE: return (cmp != 0);
|
||||
case TK_GE: return (cmp >= 0);
|
||||
case TK_GT: return (cmp > 0);
|
||||
case TK_LE: return (cmp <= 0);
|
||||
case TK_LT: return (cmp < 0);
|
||||
default: return 0;/*(should not happen)*/
|
||||
}
|
||||
}
|
||||
|
||||
__attribute_pure__
|
||||
static int ssi_eval_expr_cmp_bool(const ssi_val_t * const v1, const ssi_val_t * const v2, const int cond) {
|
||||
return (cond == TK_OR)
|
||||
? ssi_val_tobool(v1) || ssi_val_tobool(v2) /* TK_OR */
|
||||
: ssi_val_tobool(v1) && ssi_val_tobool(v2); /* TK_AND */
|
||||
}
|
||||
|
||||
static void ssi_eval_expr_append_val(buffer * const b, const char *s, const size_t slen) {
|
||||
if (buffer_is_blank(b))
|
||||
buffer_append_string_len(b, s, slen);
|
||||
else
|
||||
buffer_append_str2(b, CONST_STR_LEN(""), s, slen);
|
||||
}
|
||||
|
||||
static int ssi_expr_tokenizer(ssi_tokenizer_t * const t, buffer * const token) {
|
||||
size_t i;
|
||||
|
||||
while (t->offset < t->size
|
||||
&& (t->input[t->offset] == ' ' || t->input[t->offset] == '\t')) {
|
||||
++t->offset;
|
||||
}
|
||||
if (t->offset >= t->size)
|
||||
return 0;
|
||||
if (t->input[t->offset] == '\0') {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu foobar", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (t->input[t->offset]) {
|
||||
case '=':
|
||||
#if 0 /*(maybe accept "==", too)*/
|
||||
if (t->input[t->offset + 1] == '=')
|
||||
++t->offset;
|
||||
#endif
|
||||
t->offset++;
|
||||
return TK_EQ;
|
||||
case '>':
|
||||
if (t->input[t->offset + 1] == '=') {
|
||||
t->offset += 2;
|
||||
return TK_GE;
|
||||
} else {
|
||||
t->offset += 1;
|
||||
return TK_GT;
|
||||
}
|
||||
case '<':
|
||||
if (t->input[t->offset + 1] == '=') {
|
||||
t->offset += 2;
|
||||
return TK_LE;
|
||||
} else {
|
||||
t->offset += 1;
|
||||
return TK_LT;
|
||||
}
|
||||
case '!':
|
||||
if (t->input[t->offset + 1] == '=') {
|
||||
t->offset += 2;
|
||||
return TK_NE;
|
||||
} else {
|
||||
t->offset += 1;
|
||||
return TK_NOT;
|
||||
}
|
||||
case '&':
|
||||
if (t->input[t->offset + 1] == '&') {
|
||||
t->offset += 2;
|
||||
return TK_AND;
|
||||
} else {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing second &", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
case '|':
|
||||
if (t->input[t->offset + 1] == '|') {
|
||||
t->offset += 2;
|
||||
return TK_OR;
|
||||
} else {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing second |", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
case '(':
|
||||
t->offset++;
|
||||
t->in_brace++;
|
||||
return TK_LPARAN;
|
||||
case ')':
|
||||
t->offset++;
|
||||
t->in_brace--;
|
||||
return TK_RPARAN;
|
||||
case '\'':
|
||||
/* search for the terminating "'" */
|
||||
for (i = 1; t->input[t->offset + i] && t->input[t->offset + i] != '\''; i++);
|
||||
|
||||
if (t->input[t->offset + i]) {
|
||||
ssi_eval_expr_append_val(token, t->input + t->offset + 1, i-1);
|
||||
t->offset += i + 1;
|
||||
return TK_VALUE;
|
||||
} else {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing closing quote", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
case '$': {
|
||||
const char *var;
|
||||
size_t varlen;
|
||||
if (t->input[t->offset + 1] == '{') {
|
||||
for (i = 2; t->input[t->offset + i] && t->input[t->offset + i] != '}'; i++);
|
||||
|
||||
if (t->input[t->offset + i] != '}') {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing closing curly-brace", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
++i; /* step past '}' */
|
||||
var = t->input + t->offset + 2;
|
||||
varlen = i-3;
|
||||
} else {
|
||||
for (i = 1; isalpha(((unsigned char *)t->input)[t->offset + i]) ||
|
||||
t->input[t->offset + i] == '_' ||
|
||||
((i > 1) && isdigit(((unsigned char *)t->input)[t->offset + i])); i++);
|
||||
var = t->input + t->offset + 1;
|
||||
varlen = i-1;
|
||||
}
|
||||
|
||||
const data_string *ds;
|
||||
if (NULL != (ds = (const data_string *)array_get_element_klen(t->p->ssi_cgi_env, var, varlen))
|
||||
|| NULL != (ds = (const data_string *)array_get_element_klen(t->p->ssi_vars, var, varlen)))
|
||||
ssi_eval_expr_append_val(token, BUF_PTR_LEN(&ds->value));
|
||||
t->offset += i;
|
||||
return TK_VALUE;
|
||||
}
|
||||
default:
|
||||
for (i = 0; isgraph(((unsigned char *)t->input)[t->offset + i]); i++) {
|
||||
char d = t->input[t->offset + i];
|
||||
switch(d) {
|
||||
default: continue;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case ')':
|
||||
case '(':
|
||||
case '\'':
|
||||
case '=':
|
||||
case '!':
|
||||
case '<':
|
||||
case '>':
|
||||
case '&':
|
||||
case '|':
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ssi_eval_expr_append_val(token, t->input + t->offset, i);
|
||||
t->offset += i;
|
||||
return TK_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
static int ssi_eval_expr_loop(ssi_tokenizer_t * const t, ssi_val_t * const v);
|
||||
|
||||
static int ssi_eval_expr_step(ssi_tokenizer_t * const t, ssi_val_t * const v) {
|
||||
buffer_clear(v->str);
|
||||
v->type = SSI_TYPE_UNSET; /*(not SSI_TYPE_BOOL)*/
|
||||
int next;
|
||||
const int level = t->in_brace;
|
||||
switch ((next = ssi_expr_tokenizer(t, v->str))) {
|
||||
case TK_VALUE:
|
||||
do { next=ssi_expr_tokenizer(t, v->str); } while (next == TK_VALUE);
|
||||
return next;
|
||||
case TK_LPARAN:
|
||||
if (t->in_brace > 16) return -1; /*(arbitrary limit)*/
|
||||
next = ssi_eval_expr_loop(t, v);
|
||||
if (next == TK_RPARAN && level == t->in_brace) {
|
||||
int result = ssi_val_tobool(v);
|
||||
next = ssi_eval_expr_step(t, v); /*(resets v)*/
|
||||
v->bo = result;
|
||||
v->type = SSI_TYPE_BOOL;
|
||||
return (next==TK_AND || next==TK_OR || next==TK_RPARAN || 0==next)
|
||||
? next
|
||||
: -1;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
case TK_RPARAN:
|
||||
return t->in_brace >= 0 ? TK_RPARAN : -1;
|
||||
case TK_NOT:
|
||||
if (++t->depth > 16) return -1; /*(arbitrary limit)*/
|
||||
next = ssi_eval_expr_step(t, v);
|
||||
--t->depth;
|
||||
if (-1 == next) return next;
|
||||
v->bo = !ssi_val_tobool(v);
|
||||
v->type = SSI_TYPE_BOOL;
|
||||
return next;
|
||||
default:
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
static int ssi_eval_expr_loop_cmp(ssi_tokenizer_t * const t, ssi_val_t * const v1, int cond) {
|
||||
ssi_val_t v2 = { SSI_TYPE_UNSET, NULL, 0 };
|
||||
v2.str = buffer_init();
|
||||
int next = ssi_eval_expr_step(t, &v2);
|
||||
if (-1 != next) {
|
||||
v1->bo = ssi_eval_expr_cmp(v1, &v2, cond);
|
||||
v1->type = SSI_TYPE_BOOL;
|
||||
}
|
||||
buffer_free(v2.str);
|
||||
return next;
|
||||
}
|
||||
|
||||
static int ssi_eval_expr_loop(ssi_tokenizer_t * const t, ssi_val_t * const v1) {
|
||||
int next = ssi_eval_expr_step(t, v1);
|
||||
switch (next) {
|
||||
case TK_AND: case TK_OR:
|
||||
break;
|
||||
case TK_EQ: case TK_NE:
|
||||
case TK_GT: case TK_GE:
|
||||
case TK_LT: case TK_LE:
|
||||
next = ssi_eval_expr_loop_cmp(t, v1, next);
|
||||
if (next == TK_RPARAN || 0 == next) return next;
|
||||
if (next != TK_AND && next != TK_OR) {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu parser failed somehow near here", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return next;
|
||||
}
|
||||
|
||||
/*(Note: '&&' and '||' evaluations are not short-circuited)*/
|
||||
ssi_val_t v2 = { SSI_TYPE_UNSET, NULL, 0 };
|
||||
v2.str = buffer_init();
|
||||
do {
|
||||
int cond = next;
|
||||
next = ssi_eval_expr_step(t, &v2);
|
||||
switch (next) {
|
||||
case TK_AND: case TK_OR: case 0:
|
||||
break;
|
||||
case TK_EQ: case TK_NE:
|
||||
case TK_GT: case TK_GE:
|
||||
case TK_LT: case TK_LE:
|
||||
next = ssi_eval_expr_loop_cmp(t, &v2, next);
|
||||
if (-1 != next)
|
||||
break;
|
||||
__attribute_fallthrough__
|
||||
default:
|
||||
buffer_free(v2.str);
|
||||
return next;
|
||||
}
|
||||
v1->bo = ssi_eval_expr_cmp_bool(v1, &v2, cond);
|
||||
v1->type = SSI_TYPE_BOOL;
|
||||
} while (next == TK_AND || next == TK_OR);
|
||||
buffer_free(v2.str);
|
||||
return next;
|
||||
}
|
||||
|
||||
int ssi_eval_expr(handler_ctx *p, const char *expr) {
|
||||
ssi_tokenizer_t t;
|
||||
t.input = expr;
|
||||
t.offset = 0;
|
||||
t.size = strlen(expr);
|
||||
t.in_brace = 0;
|
||||
t.depth = 0;
|
||||
t.p = p;
|
||||
|
||||
ssi_val_t v = { SSI_TYPE_UNSET, NULL, 0 };
|
||||
v.str = buffer_init();
|
||||
int rc = ssi_eval_expr_loop(&t, &v);
|
||||
rc = (0 == rc && 0 == t.in_brace && 0 == t.depth)
|
||||
? ssi_val_tobool(&v)
|
||||
: -1;
|
||||
buffer_free(v.str);
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -8,7 +8,6 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "mod_ssi.c"
|
||||
#include "mod_ssi_expr.c"
|
||||
#include "fdlog.h"
|
||||
|
||||
static void test_mod_ssi_reset (request_st * const r, handler_ctx * const hctx)
|
||||
|
|
Loading…
Reference in New Issue