Browse Source

user defined variable, compute on parsing: string+string, int+int, array+array, var+=expression.

"include" sub configuration file. (merged ([308], [309], [306], [305])


git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-merge-1.4.x@520 152afb58-edef-0310-8abb-c4023f1b3aa9
svn/tags/lighttpd-1.4.2
Jan Kneschke 16 years ago
parent
commit
6e78c2c8df
  1. 6
      configure.in
  2. 22
      doc/configuration.txt
  3. 18
      doc/lighttpd.conf
  4. 34
      src/array.c
  5. 3
      src/array.h
  6. 6
      src/buffer.c
  7. 1
      src/buffer.h
  8. 2
      src/configfile-glue.c
  9. 290
      src/configfile.c
  10. 4
      src/configfile.h
  11. 230
      src/configparser.y
  12. 11
      src/data_array.c
  13. 11
      src/data_config.c
  14. 11
      src/data_count.c
  15. 10
      src/data_fastcgi.c
  16. 10
      src/data_integer.c
  17. 10
      src/data_string.c
  18. 5
      src/server.c
  19. 3
      tests/Makefile.am
  20. 16
      tests/condition.conf
  21. 8
      tests/core-condition.t
  22. 50
      tests/core-var-include.t
  23. 2
      tests/docroot/www/Makefile.am
  24. 1
      tests/prepare.sh
  25. 24
      tests/var-include-sub.conf
  26. 41
      tests/var-include.conf

6
configure.in

@ -96,7 +96,11 @@ AC_ARG_WITH(mysql,
if test \! -x $withval; then
echo "--with-mysql=path-to-mysql_config"
fi
MYSQL_INCLUDE="`$withval --cflags | sed s/\'//g`"
if $withval | grep -- '--include' ; then
MYSQL_INCLUDE="`$withval --include | sed s/\'//g`"
else
MYSQL_INCLUDE="`$withval --cflags | sed s/\'//g`"
fi
MYSQL_LIBS="`$withval --libs | sed s/\'//g`"
AC_MSG_RESULT(yes)

22
doc/configuration.txt

@ -26,13 +26,15 @@ Basic Syntax
A BNF like notation: ::
option : NAME = VARIABLE
option : NAME = VALUE
merge : NAME += VALUE
NAME : modulename.key
VARIABLE : ( <string> | <integer> | <boolean> | <array> )
VALUE : ( <string> | <integer> | <boolean> | <array> | VALUE [ + VALUE ]*)
<string> : "text"
<integer>: digit*
<boolean>: ( "enable" | "disable" )
<array> : "(" [ <string> "=>" ] <variable> [, [ <string> "=>" ] <variable> ]* ")"
<array> : "(" [ <string> "=>" ] <value> [, [ <string> "=>" ] <value> ]* ")"
INCLUDE : "include" VALUE
Example
-------
@ -50,7 +52,13 @@ Example
# enable directory listings
server.dir-listing = "enable"
# variables, computed when config is read.
var.mymodule = "foo"
server.modules += ( "mod_" + var.mymodule )
# include, relative to dirname of main config file
include "mime.types.conf"
Conditional Configuration
@ -63,6 +71,12 @@ Most options can be configured conditionally by using the following syntax
<field> <operator> <value> {
...
<field> <operator> <value> {
... nesting: match only when parent match
}
}
| <field> <operator> <value> {
... else if
}
where <field> is one of one of the following:

18
doc/lighttpd.conf

@ -283,3 +283,21 @@ $HTTP["url"] =~ "\.pdf$" {
## don't forget to add index.cml to server.indexfiles
# cml.extension = ".cml"
# cml.memcache-hosts = ( "127.0.0.1:11211" )
#### variable usage:
## variable name without "." is auto prefixed by "var." and becomes "var.bar"
#bar = 1
#var.mystring = "foo"
## integer add
#bar += 1
## string concat, with integer cast as string, result: "www.foo1.com"
#server.name = "www." + mystring + var.bar + ".com"
## array merge
#index-file.names = (foo + ".php") + index-file.names
#index-file.names += (foo + ".php")
#### include
#include /etc/lighttpd/lighttpd-inc.conf
## same as above if you run: "lighttpd -f /etc/lighttpd/lighttpd.conf"
#include "lighttpd-inc.conf"

34
src/array.c

@ -20,6 +20,26 @@ array *array_init(void) {
return a;
}
array *array_init_array(array *src) {
size_t i;
array *a = array_init();
a->used = src->used;
a->size = src->size;
a->next_power_of_2 = src->next_power_of_2;
a->unique_ndx = src->unique_ndx;
a->data = malloc(sizeof(*src->data) * src->size);
for (i = 0; i < src->size; i++) {
if (src->data[i]) a->data[i] = src->data[i]->copy(src->data[i]);
else a->data[i] = NULL;
}
a->sorted = malloc(sizeof(*src->sorted) * src->size);
memcpy(a->sorted, src->sorted, sizeof(*src->sorted) * src->size);
return a;
}
void array_free(array *a) {
size_t i;
if (!a) return;
@ -122,6 +142,20 @@ data_unset *array_get_unused_element(array *a, data_type_t t) {
return ds;
}
/* replace or insert data, return the old one with the same key */
data_unset *array_replace(array *a, data_unset *du) {
int ndx;
if (-1 == (ndx = array_get_index(a, du->key->ptr, du->key->used, NULL))) {
array_insert_unique(a, du);
return NULL;
}
else {
data_unset *old = a->data[ndx];
a->data[ndx] = du;
return old;
}
}
int array_insert_unique(array *a, data_unset *str) {
int ndx = -1;
int pos = 0;

3
src/array.h

@ -14,6 +14,7 @@ typedef enum { TYPE_UNSET, TYPE_STRING, TYPE_COUNT, TYPE_ARRAY, TYPE_INTEGER, TY
#define DATA_UNSET \
data_type_t type; \
buffer *key; \
struct data_unset *(*copy)(struct data_unset *src); \
void (* free)(struct data_unset *p); \
void (* reset)(struct data_unset *p); \
int (*insert_dup)(struct data_unset *dst, struct data_unset *src); \
@ -120,6 +121,7 @@ typedef struct {
data_fastcgi *data_fastcgi_init(void);
array *array_init(void);
array *array_init_array(array *a);
void array_free(array *a);
void array_reset(array *a);
int array_insert_unique(array *a, data_unset *str);
@ -127,6 +129,7 @@ data_unset *array_pop(array *a);
int array_print(array *a, int depth);
data_unset *array_get_unused_element(array *a, data_type_t t);
data_unset *array_get_element(array *a, const char *key);
data_unset *array_replace(array *a, data_unset *du);
int array_strcasecmp(const char *a, size_t a_len, const char *b, size_t b_len);
void array_print_indent(int depth);

6
src/buffer.c

@ -29,6 +29,12 @@ buffer* buffer_init(void) {
return b;
}
buffer *buffer_init_buffer(buffer *src) {
buffer *b = buffer_init();
buffer_copy_string_buffer(b, src);
return b;
}
/**
* free the buffer
*

1
src/buffer.h

@ -39,6 +39,7 @@ void buffer_array_reset(buffer_array *b);
buffer *buffer_array_append_get_buffer(buffer_array *b);
buffer* buffer_init(void);
buffer* buffer_init_buffer(buffer *b);
buffer* buffer_init_string(const char *str);
void buffer_free(buffer *b);
void buffer_reset(buffer *b);

2
src/configfile-glue.c

@ -153,7 +153,7 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat
buffer *l;
server_socket *srv_sock = con->srv_socket;
/* check parent first */
if (dc->parent) {
if (dc->parent && dc->parent->context_ndx) {
if (con->conf.log_condition_handling) {
log_error_write(srv, __FILE__, __LINE__, "sb", "go parent", dc->parent->string);
}

290
src/configfile.c

@ -355,7 +355,9 @@ typedef struct {
int foo;
int bar;
char *input;
buffer *file;
stream s;
const char *input;
size_t offset;
size_t size;
@ -367,6 +369,42 @@ typedef struct {
int in_cond;
} tokenizer_t;
static int tokenizer_open(server *srv, tokenizer_t *t, buffer *basedir, const char *fn) {
if (buffer_is_empty(basedir) &&
(fn[0] == '/' || fn[0] == '\\') &&
(fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) {
t->file = buffer_init_string(fn);
} else {
t->file = buffer_init_buffer(basedir);
buffer_append_string(t->file, fn);
}
if (0 != stream_open(&(t->s), t->file)) {
log_error_write(srv, __FILE__, __LINE__, "sbss",
"opening configfile ", t->file, "failed:", strerror(errno));
buffer_free(t->file);
return -1;
}
t->input = t->s.start;
t->offset = 0;
t->size = t->s.size;
t->line = 1;
t->line_pos = 1;
t->in_key = 1;
t->in_brace = 0;
t->in_cond = 0;
return 0;
}
static int tokenizer_close(server *srv, tokenizer_t *t) {
UNUSED(srv);
buffer_free(t->file);
return stream_close(&(t->s));
}
static int config_skip_newline(tokenizer_t *t) {
int skipped = 1;
assert(t->input[t->offset] == '\r' || t->input[t->offset] == '\n');
@ -394,7 +432,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
for (tid = 0; tid == 0 && t->offset < t->size && t->input[t->offset] ; ) {
char c = t->input[t->offset];
char *start = NULL;
const char *start = NULL;
switch (c) {
case '=':
@ -406,7 +444,8 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
tid = TK_ARRAY_ASSIGN;
} else {
log_error_write(srv, __FILE__, __LINE__, "sdsds",
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos,
"use => for assignments in arrays");
return -1;
@ -425,11 +464,14 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
tid = TK_MATCH;
} else {
log_error_write(srv, __FILE__, __LINE__, "sdsds",
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos,
"only =~ and == are allow in the condition");
return -1;
}
t->in_key = 1;
t->in_cond = 0;
} else if (t->in_key) {
tid = TK_ASSIGN;
@ -437,9 +479,9 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
t->offset++;
t->line_pos++;
t->in_key = 0;
} else {
log_error_write(srv, __FILE__, __LINE__, "sdsds",
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos,
"unexpected equal-sign: =");
return -1;
@ -461,13 +503,17 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
tid = TK_NOMATCH;
} else {
log_error_write(srv, __FILE__, __LINE__, "sdsds",
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos,
"only !~ and != are allow in the condition");
return -1;
}
t->in_key = 1;
t->in_cond = 0;
} else {
log_error_write(srv, __FILE__, __LINE__, "sdsds",
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos,
"unexpected exclamation-marks: !");
return -1;
@ -512,7 +558,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
} else {
config_skip_newline(t);
t->line_pos = 1;
t->line++;
t->line++;
}
break;
case ',':
@ -556,7 +602,8 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
if (t->input[t->offset + i] == '\0') {
/* ERROR */
log_error_write(srv, __FILE__, __LINE__, "sdsds",
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos,
"missing closing quote");
@ -593,6 +640,20 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
buffer_copy_string(token, "$");
break;
case '+':
if (t->input[t->offset + 1] == '=') {
t->offset += 2;
buffer_copy_string(token, "+=");
tid = TK_APPEND;
}
else {
t->offset++;
tid = TK_PLUS;
buffer_copy_string(token, "+");
}
break;
case '|':
t->offset++;
tid = TK_OR;
@ -603,8 +664,6 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
t->offset++;
tid = TK_LCURLY;
t->in_key = 1;
t->in_cond = 0;
buffer_copy_string(token, "{");
@ -641,72 +700,71 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
break;
default:
if (t->in_key) {
/* the key might consist of [-.0-9a-z] */
if (t->in_cond) {
for (i = 0; t->input[t->offset + i] &&
(isalnum((unsigned char)t->input[t->offset + i]) ||
t->input[t->offset + i] == '.' ||
t->input[t->offset + i] == '-'
(isalpha((unsigned char)t->input[t->offset + i])
); i++);
if (i && t->input[t->offset + i]) {
tid = TK_LKEY;
tid = TK_SRVVARNAME;
buffer_copy_string_len(token, t->input + t->offset, i);
t->offset += i;
t->line_pos += i;
} else {
/* ERROR */
log_error_write(srv, __FILE__, __LINE__, "sdsds",
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos,
"invalid character in lvalue");
"invalid character in condition");
return -1;
}
} else if (t->in_cond) {
for (i = 0; t->input[t->offset + i] &&
(isalpha((unsigned char)t->input[t->offset + i])
); i++);
} else if (isdigit((unsigned char)c)) {
/* take all digits */
for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]); i++);
/* was there it least a digit ? */
if (i && t->input[t->offset + i]) {
tid = TK_SRVVARNAME;
tid = TK_INTEGER;
buffer_copy_string_len(token, t->input + t->offset, i);
t->offset += i;
t->line_pos += i;
} else {
/* ERROR */
log_error_write(srv, __FILE__, __LINE__, "sdsds",
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos,
"invalid character in condition");
"unexpected EOF");
return -1;
}
} else {
if (isdigit((unsigned char)c)) {
/* take all digits */
for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]); i++);
/* the key might consist of [-.0-9a-z] */
for (i = 0; t->input[t->offset + i] &&
(isalnum((unsigned char)t->input[t->offset + i]) ||
t->input[t->offset + i] == '.' ||
t->input[t->offset + i] == '-'
); i++);
if (i && t->input[t->offset + i]) {
buffer_copy_string_len(token, t->input + t->offset, i);
/* was there it least a digit ? */
if (i && t->input[t->offset + i]) {
tid = TK_INTEGER;
buffer_copy_string_len(token, t->input + t->offset, i);
t->offset += i;
t->line_pos += i;
if (strcmp(token->ptr, "include") == 0) {
tid = TK_INCLUDE;
} else {
/* ERROR */
log_error_write(srv, __FILE__, __LINE__, "sdsds",
"line:", t->line, "pos:", t->line_pos,
"unexpected EOF");
return -1;
tid = TK_LKEY;
}
t->offset += i;
t->line_pos += i;
} else {
/* ERROR */
log_error_write(srv, __FILE__, __LINE__, "sdsds",
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos,
"invalid value field");
"invalid character in variable name");
return -1;
}
}
@ -716,6 +774,11 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
if (tid) {
*token_id = tid;
#if 0
log_error_write(srv, __FILE__, __LINE__, "sbsdsdb",
"file:", t->file,
"line:", t->line, "pos:", t->line_pos, token);
#endif
return 1;
} else if (t->offset < t->size) {
@ -726,40 +789,88 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
return 0;
}
int config_read(server *srv, const char *fn) {
stream s;
int config_parse_file(server *srv, config_t *context, const char *fn) {
tokenizer_t t;
void *pParser;
int token_id;
buffer *token;
config_t context;
data_config *dc;
buffer *token, *lasttoken;
int ret;
buffer *bfn = buffer_init_string(fn);
if (tokenizer_open(srv, &t, context->basedir, fn) == -1) {
return -1;
}
if (0 != stream_open(&s, bfn)) {
buffer_free(bfn);
pParser = configparserAlloc( malloc );
lasttoken = buffer_init();
token = buffer_init();
while((1 == (ret = config_tokenizer(srv, &t, &token_id, token))) && context->ok) {
buffer_copy_string_buffer(lasttoken, token);
configparser(pParser, token_id, token, context);
log_error_write(srv, __FILE__, __LINE__, "ssss",
"opening configfile ", fn, "failed:", strerror(errno));
return -1;
token = buffer_init();
}
if (ret != -1 && context->ok) {
/* add an EOL at EOF, better than say sorry */
buffer_copy_string(token, "(EOL)");
configparser(pParser, TK_EOL, token, context);
token = buffer_init_string("(END)");
if (context->ok) {
configparser(pParser, 0, token, context);
token = buffer_init();
}
}
configparserFree(pParser, free);
buffer_free(token);
buffer_free(bfn);
if (ret == -1) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"configfile parser failed:", lasttoken);
}
else if (context->ok == 0) {
log_error_write(srv, __FILE__, __LINE__, "sbsdsdsb",
"file:", t.file,
"line:", t.line, "pos:", t.line_pos,
"parser failed somehow near here:", lasttoken);
ret = -1;
}
buffer_free(lasttoken);
t.input = s.start;
t.offset = 0;
t.size = s.size;
t.line = 1;
t.line_pos = 1;
t.in_key = 1;
t.in_brace = 0;
t.in_cond = 0;
context.ok = 1;
tokenizer_close(srv, &t);
return ret == -1 ? -1 : 0;
}
static void context_init(server *srv, config_t *context) {
context->srv = srv;
context->ok = 1;
context->configs_stack = array_init();
context->basedir = buffer_init();
}
static void context_free(config_t *context) {
array_free(context->configs_stack);
buffer_free(context->basedir);
}
int config_read(server *srv, const char *fn) {
config_t context;
data_config *dc;
int ret;
char *pos;
context_init(srv, &context);
context.all_configs = srv->config_context;
context.configs_stack = array_init();
pos = strrchr(fn,
#ifdef __WIN32
'\\'
#else
'/'
#endif
);
if (pos) {
buffer_copy_string_len(context.basedir, fn, pos - fn + 1);
fn = pos + 1;
}
dc = data_config_init();
buffer_copy_string(dc->key, "global");
@ -768,39 +879,18 @@ int config_read(server *srv, const char *fn) {
dc->context_ndx = context.all_configs->used;
array_insert_unique(context.all_configs, (data_unset *)dc);
context.current = dc;
/* default context */
srv->config = dc->value;
pParser = configparserAlloc( malloc );
token = buffer_init();
while((1 == (ret = config_tokenizer(srv, &t, &token_id, token))) && context.ok) {
configparser(pParser, token_id, token, &context);
token = buffer_init();
}
configparser(pParser, 0, token, &context);
configparserFree(pParser, free );
buffer_free(token);
stream_close(&s);
if (ret == -1) {
log_error_write(srv, __FILE__, __LINE__, "s",
"configfile parser failed");
return -1;
}
if (context.ok == 0) {
log_error_write(srv, __FILE__, __LINE__, "sdsds",
"line:", t.line, "pos:", t.line_pos,
"parser failed somehow near here");
return -1;
ret = config_parse_file(srv, &context, fn);
assert(0 != ret || context.configs_stack->used == 0);
context_free(&context);
if (0 != ret) {
return ret;
}
assert(context.configs_stack->used == 0);
array_free(context.configs_stack);
if (0 != config_insert(srv)) {
return -1;

4
src/configfile.h

@ -3,16 +3,20 @@
#include "array.h"
#include "buffer.h"
#include "server.h"
typedef struct {
server *srv;
int ok;
array *all_configs;
array *configs_stack; /* to parse nested block */
data_config *current; /* current started with { */
buffer *basedir;
} config_t;
void *configparserAlloc(void *(*mallocProc)(size_t));
void configparserFree(void *p, void (*freeProc)(void*));
void configparser(void *yyp, int yymajor, buffer *yyminor, config_t *ctx);
int config_parse_file(server *srv, config_t *context, const char *fn);
#endif

230
src/configparser.y

@ -6,6 +6,7 @@
%include {
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "configfile.h"
#include "buffer.h"
@ -14,7 +15,9 @@
static void configparser_push(config_t *ctx, data_config *dc, int isnew) {
if (isnew) {
dc->context_ndx = ctx->all_configs->used;
assert(dc->context_ndx > ctx->current->context_ndx);
array_insert_unique(ctx->all_configs, (data_unset *)dc);
dc->parent = ctx->current;
}
array_insert_unique(ctx->configs_stack, (data_unset *)ctx->current);
ctx->current = dc;
@ -26,6 +29,85 @@ static data_config *configparser_pop(config_t *ctx) {
return old;
}
data_unset *configparser_get_variable(config_t *ctx, buffer *key) {
data_unset *ds, *result;
data_config *dc;
result = NULL;
#if 0
fprintf(stderr, "get var %s\n", key->ptr);
#endif
for (dc = ctx->current; dc && !result; dc = dc->parent) {
#if 0
fprintf(stderr, "get var on block: %s\n", dc->key->ptr);
#endif
ds = array_get_element(dc->value, key->ptr);
if (NULL != ds) {
result = ds;
break;
}
}
if (NULL == result) {
fprintf(stderr, "Undefined config variable: %s\n", key->ptr);
ctx->ok = 0;
}
return result;
}
/* op1 is to be eat/return by this function, op1->key is not cared
op2 is left untouch, unreferenced
*/
data_unset *configparser_merge_data(config_t *ctx, data_unset *op1, const data_unset *op2) {
/* type mismatch */
if (op1->type != op2->type) {
if (op1->type == TYPE_STRING && op2->type == TYPE_INTEGER) {
data_string *ds = (data_string *)op1;
buffer_append_long(ds->value, ((data_integer*)op2)->value);
return op1;
}
else if (op1->type == TYPE_INTEGER && op2->type == TYPE_STRING) {
data_string *ds = data_string_init();
buffer_append_long(ds->value, ((data_integer*)op1)->value);
buffer_append_string_buffer(ds->value, ((data_string*)op2)->value);
op1->free(op1);
return (data_unset *)ds;
}
else {
fprintf(stderr, "data type mismatch, cannot be merge\n");
ctx->ok = 0;
op1->free(op1);
return NULL;
}
}
switch (op1->type) {
case TYPE_STRING:
buffer_append_string_buffer(((data_string *)op1)->value, ((data_string *)op2)->value);
break;
case TYPE_INTEGER:
((data_integer *)op1)->value += ((data_integer *)op2)->value;
break;
case TYPE_ARRAY: {
array *dst = ((data_array *)op1)->value;
array *src = ((data_array *)op2)->value;
data_unset *du;
size_t i;
for (i = 0; i < src->used; i ++) {
du = (data_unset *)src->data[i];
if (du) {
array_insert_unique(dst, du->copy(du));
}
}
break;
default:
assert(0);
break;
}
}
return op1;
}
}
%parse_failure {
@ -37,9 +119,12 @@ metalines ::= metalines metaline.
metalines ::= .
metaline ::= varline.
metaline ::= condlines EOL.
metaline ::= include.
metaline ::= EOL.
%type value {data_unset *}
%type expression {data_unset *}
%type context_rvalue {data_unset *}
%type aelement {data_unset *}
%type aelements {array *}
%type array {array *}
@ -48,7 +133,7 @@ metaline ::= EOL.
%type cond {config_cond_t }
%token_destructor { buffer_free($$); }
varline ::= key(A) ASSIGN value(B). {
varline ::= key(A) ASSIGN expression(B). {
buffer_copy_string_buffer(B->key, A);
if (NULL == array_get_element(ctx->current->value, B->key->ptr)) {
array_insert_unique(ctx->current->value, B);
@ -61,11 +146,63 @@ varline ::= key(A) ASSIGN value(B). {
buffer_free(A);
}
varline ::= key(A) APPEND expression(B). {
array *vars = ctx->current->value;
data_unset *du;
if (NULL == (du = configparser_get_variable(ctx, A))) {
fprintf(stderr, "Undefined config variable in conditional 1 %s: %s\n",
ctx->current->key->ptr, A->ptr);
ctx->ok = 0;
} else if (NULL != (du = array_get_element(vars, A->ptr))) {
/* exists in current block */
du = configparser_merge_data(ctx, du, B);
buffer_copy_string_buffer(du->key, A);
array_replace(vars, du);
} else {
du = configparser_merge_data(ctx, du->copy(du), B);
buffer_copy_string_buffer(du->key, A);
array_insert_unique(ctx->current->value, du);
}
buffer_free(A);
A = NULL;
B->free(B);
B = NULL;
}
key(A) ::= LKEY(B). {
if (strchr(B->ptr, '.') == NULL) {
A = buffer_init_string("var.");
buffer_append_string_buffer(A, B);
}
else {
A = B;
B = NULL;
}
}
expression(A) ::= expression(B) PLUS value(C). {
A = configparser_merge_data(ctx, B, C);
B = NULL;
C->free(C);
C = NULL;
}
expression(A) ::= value(B). {
A = B;
B = NULL;
}
value(A) ::= key(B). {
A = configparser_get_variable(ctx, B);
if (!A) {
/* make a dummy so it won't crash */
A = (data_unset *)data_string_init();
}
buffer_free(B);
B = NULL;
}
value(A) ::= STRING(B). {
A = (data_unset *)data_string_init();
buffer_copy_string_buffer(((data_string *)(A))->value, B);
@ -110,11 +247,11 @@ aelements(A) ::= aelement(B). {
array_insert_unique(A, B);
}
aelement(A) ::= value(B). {
aelement(A) ::= expression(B). {
A = B;
B = NULL;
}
aelement(A) ::= STRING(B) ARRAY_ASSIGN value(C). {
aelement(A) ::= STRING(B) ARRAY_ASSIGN expression(C). {
buffer_copy_string_buffer(C->key, B);
buffer_free(B);
@ -140,32 +277,49 @@ condlines(A) ::= condline(B). {
}
condline(A) ::= context LCURLY metalines RCURLY. {
data_config *parent, *cur;
data_config *cur;
cur = ctx->current;
configparser_pop(ctx);
parent = ctx->current;
assert(cur && parent);
assert(cur && ctx->current);
if (0 != parent->context_ndx) { /* not global */
assert(cur->context_ndx > parent->context_ndx);
cur->parent = parent;
}
A = cur;
}
context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) STRING(D). {
context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) expression(D). {
data_config *dc;
buffer *b;
buffer *b, *rvalue;
if (ctx->ok && D->type != TYPE_STRING) {
fprintf(stderr, "rvalue must be string");
ctx->ok = 0;
}
b = buffer_init();
buffer_copy_string_buffer(b, ctx->current->key);
buffer_append_string(b, "/");
buffer_append_string_buffer(b, B);
buffer_append_string_buffer(b, C);
buffer_append_string_buffer(b, D);
buffer_append_long(b, E);
switch(E) {
case CONFIG_COND_NE:
buffer_append_string_len(b, CONST_STR_LEN("!="));
break;
case CONFIG_COND_EQ:
buffer_append_string_len(b, CONST_STR_LEN("=="));
break;
case CONFIG_COND_NOMATCH:
buffer_append_string_len(b, CONST_STR_LEN("!~"));
break;
case CONFIG_COND_MATCH:
buffer_append_string_len(b, CONST_STR_LEN("=~"));
break;
default:
buffer_append_string_len(b, CONST_STR_LEN("??"));
break;
}
rvalue = ((data_string*)D)->value;
buffer_append_string_buffer(b, rvalue);
if (NULL != (dc = (data_config *)array_get_element(ctx->all_configs, b->ptr))) {
configparser_push(ctx, dc, 0);
@ -180,7 +334,7 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) STRING(D).
switch(E) {
case CONFIG_COND_NE:
case CONFIG_COND_EQ:
dc->string = buffer_init_string(D->ptr);
dc->string = buffer_init_buffer(rvalue);
break;
case CONFIG_COND_NOMATCH:
case CONFIG_COND_MATCH: {
@ -189,16 +343,22 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) STRING(D).
int erroff;
if (NULL == (dc->regex =
pcre_compile(D->ptr, 0, &errptr, &erroff, NULL))) {
dc->string = buffer_init_string(errptr);
dc->cond = CONFIG_COND_UNSET;
ctx->ok = 0;
} else if (NULL == (dc->regex_study = pcre_study(dc->regex, 0, &errptr)) &&
pcre_compile(rvalue->ptr, 0, &errptr, &erroff, NULL))) {
dc->string = buffer_init_string(errptr);
dc->cond = CONFIG_COND_UNSET;
fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n",
rvalue->ptr, errptr, erroff);
ctx->ok = 0;
} else if (NULL == (dc->regex_study =
pcre_study(dc->regex, 0, &errptr)) &&
errptr != NULL) {
fprintf(stderr, "studying regex failed: %s -> %s\n",
D->ptr, errptr);
ctx->ok = 0;
rvalue->ptr, errptr);
ctx->ok = 0;
} else {
dc->string = buffer_init_buffer(rvalue);
}
#else
fprintf(stderr, "regex conditionals are not allowed as pcre-support" \
@ -218,10 +378,10 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) STRING(D).
configparser_push(ctx, dc, 1);
}
buffer_free(b);
buffer_free(B);
buffer_free(C);
buffer_free(D);
D->free(D);
D = NULL;
}
cond(A) ::= EQ. {
A = CONFIG_COND_EQ;
@ -235,3 +395,19 @@ cond(A) ::= NE. {
cond(A) ::= NOMATCH. {
A = CONFIG_COND_NOMATCH;
}
include ::= INCLUDE expression(A). {
if (ctx->ok) {
if (A->type != TYPE_STRING) {
fprintf(stderr, "file must be string");
ctx->ok = 0;
} else {
buffer *file = ((data_string*)A)->value;
if (0 != config_parse_file(ctx->srv, ctx, file->ptr)) {
ctx->ok = 0;
}
}
A->free(A);
}
A = NULL;
}

11
src/data_array.c

@ -4,6 +4,15 @@
#include "array.h"
static data_unset *data_array_copy(data_unset *s) {
data_array *src = (data_array *)s;
data_array *ds = data_array_init();
ds->key = buffer_init_buffer(src->key);
ds->value = array_init_array(src->value);
return (data_unset *)ds;
}
static void data_array_free(data_unset *d) {
data_array *ds = (data_array *)d;
@ -39,7 +48,6 @@ static void data_array_print(data_unset *d, int depth) {
fprintf(stderr, "}");
}
data_array *data_array_init(void) {
data_array *ds;
@ -48,6 +56,7 @@ data_array *data_array_init(void) {
ds->key = buffer_init();
ds->value = array_init();
ds->copy = data_array_copy;
ds->free = data_array_free;
ds->reset = data_array_reset;
ds->insert_dup = data_array_insert_dup;

11
src/data_config.c

@ -4,6 +4,16 @@
#include "array.h"
static data_unset *data_config_copy(data_unset *s) {
data_config *src = (data_config *)s;
data_config *ds = data_config_init();
ds->key = buffer_init_buffer(src->key);
ds->comp_key = buffer_init_buffer(src->comp_key);
ds->value = array_init_array(src->value);
return (data_unset *)ds;
}
static void data_config_free(data_unset *d) {
data_config *ds = (data_config *)d;
@ -58,6 +68,7 @@ data_config *data_config_init(void) {
ds->comp_key = buffer_init();
ds->value = array_init();
ds->copy = data_config_copy;
ds->free = data_config_free;
ds->reset = data_config_reset;
ds->insert_dup = data_config_insert_dup;

11
src/data_count.c

@ -4,6 +4,15 @@
#include "array.h"
static data_unset *data_count_copy(data_unset *s) {
data_count *src = (data_count *)s;
data_count *ds = data_count_init();
ds->key = buffer_init_buffer(src->key);
ds->count = src->count;
return (data_unset *)ds;
}
static void data_count_free(data_unset *d) {
data_count *ds = (data_count *)d;
@ -47,6 +56,8 @@ data_count *data_count_init(void) {
ds->key = buffer_init();
ds->count = 1;
ds->copy = data_count_copy;
ds->copy = data_count_copy;
ds->free = data_count_free;
ds->reset = data_count_reset;
ds->insert_dup = data_count_insert_dup;

10
src/data_fastcgi.c

@ -5,6 +5,15 @@
#include "array.h"
#include "fastcgi.h"
static data_unset *data_fastcgi_copy(data_unset *s) {
data_fastcgi *src = (data_fastcgi *)s;
data_fastcgi *ds = data_fastcgi_init();
ds->key = buffer_init_buffer(src->key);
ds->host = buffer_init_buffer(src->host);
return (data_unset *)ds;
}
static void data_fastcgi_free(data_unset *d) {
data_fastcgi *ds = (data_fastcgi *)d;
@ -48,6 +57,7 @@ data_fastcgi *data_fastcgi_init(void) {
ds->port = 0;
ds->is_disabled = 0;
ds->copy = data_fastcgi_copy;
ds->free = data_fastcgi_free;
ds->reset = data_fastcgi_reset;
ds->insert_dup = data_fastcgi_insert_dup;

10
src/data_integer.c

@ -4,6 +4,15 @@
#include "array.h"
static data_unset *data_integer_copy(data_unset *s) {
data_integer *src = (data_integer *)s;
data_integer *ds = data_integer_init();
ds->key = buffer_init_buffer(src->key);
ds->value = src->value;
return (data_unset *)ds;
}
static void data_integer_free(data_unset *d) {
data_integer *ds = (data_integer *)d;
@ -44,6 +53,7 @@ data_integer *data_integer_init(void) {
ds->key = buffer_init();
ds->value = 0;
ds->copy = data_integer_copy;
ds->free = data_integer_free;
ds->reset = data_integer_reset;
ds->insert_dup = data_integer_insert_dup;

10
src/data_string.c

@ -5,6 +5,15 @@
#include "array.h"
static data_unset *data_string_copy(data_unset *s) {
data_string *src = (data_string *)s;
data_string *ds = data_string_init();
ds->key = buffer_init_buffer(src->key);
ds->value = buffer_init_buffer(src->value);
return (data_unset *)ds;
}
static void data_string_free(data_unset *d) {
data_string *ds = (data_string *)d;
@ -74,6 +83,7 @@ data_string *data_string_init(void) {
ds->key = buffer_init();
ds->value = buffer_init();
ds->copy = data_string_copy;
ds->free = data_string_free;
ds->reset = data_string_reset;
ds->insert_dup = data_string_insert_dup;

5
src/server.c

@ -629,6 +629,11 @@ int main (int argc, char **argv) {
for (i = 0; srv->config && i < srv->config->used; i++) {
data_unset *du = srv->config->data[i];
/* all var.* is known as user defined variable */
if (strncmp(du->key->ptr, "var.", sizeof("var.") - 1) == 0) {
continue;
}
if (NULL == array_get_element(srv->config_touched, du->key->ptr)) {
log_error_write(srv, __FILE__, __LINE__, "sbs",
"WARNING: unknown config-key:",

3
tests/Makefile.am

@ -22,6 +22,9 @@ CONFS=fastcgi-10.conf \
fastcgi-13.conf \
bug-06.conf \
bug-12.conf \
core-var-include.t \
var-include.conf \
var-include-sub.conf \
condition.conf \
core-condition.t \
core-request.t \

16
tests/condition.conf

@ -16,7 +16,7 @@ server.tag = "Apache 1.3.29"
server.modules = (
"mod_access",
"mod_redirect",
"mod_accesslog" )
######################## MODULE CONFIG ############################
@ -26,23 +26,23 @@ accesslog.filename = "/tmp/lighttpd/logs/lighttpd.access.log"
mimetype.assign = ( ".html" => "text/html" )
# ban first, unban later
url.access-deny = ( "index.html" )
url.redirect = ("^" => "/default")
$HTTP["host"] == "www.example.org" {
server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/"
server.name = "www.example.org"
url.redirect = ("^" => "/match_1")
}
| $HTTP["host"] == "test1.example.org" {
server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/"
server.name = "test1.example.org"
url.access-deny = ( "nothing" )
url.redirect = ("^" => "/match_2")
}
# comments
| $HTTP["host"] == "test2.example.org" {
server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/"
server.name = "test2.example.org"
url.access-deny = ( "nothing" )
url.redirect = ("^" => "/match_3")
}
# comments
@ -50,10 +50,10 @@ $HTTP["host"] == "www.example.org" {
| $HTTP["host"] == "test3.example.org" {
server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/"
server.name = "test3.example.org"
# comments
url.access-deny = ( "nothing" )
url.redirect = ("^" => "/match_4")
# comments
$HTTP["url"] == "/index.html" {
url.access-deny = ( "index.html" )
url.redirect = ("^" => "/match_5")
}
}

8
tests/core-condition.t

@ -22,7 +22,7 @@ GET /index.html HTTP/1.0
Host: www.example.org
EOF
);
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } );
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => "/match_1" } );
ok($tf->handle_http($t) == 0, 'config deny');
$t->{REQUEST} = ( <<EOF
@ -30,7 +30,7 @@ GET /index.html HTTP/1.0
Host: test1.example.org
EOF
);
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } );
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => "/match_2" } );
ok($tf->handle_http($t) == 0, '2nd child of chaining');
$t->{REQUEST} = ( <<EOF
@ -38,7 +38,7 @@ GET /index.html HTTP/1.0
Host: test2.example.org
EOF
);
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } );
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => "/match_3" } );
ok($tf->handle_http($t) == 0, '3rd child of chaining');
$t->{REQUEST} = ( <<EOF
@ -46,7 +46,7 @@ GET /index.html HTTP/1.0
Host: test3.example.org
EOF
);
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } );
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => "/match_5" } );
ok($tf->handle_http($t) == 0, 'nesting');
ok($tf->stop_proc == 0, "Stopping lighttpd");

50
tests/core-var-include.t

@ -0,0 +1,50 @@
#! /usr/bin/perl -w
BEGIN {
# add current source dir to the include-path
# we need this for make distcheck
(my $srcdir = $0) =~ s#/[^/]+$#/#;
unshift @INC, $srcdir;
}
use strict;
use IO::Socket;
use Test::More tests => 16;
use LightyTest;
my $tf = LightyTest->new();
my $t;
$tf->{CONFIGFILE} = 'var-include.conf';
ok($tf->start_proc == 0, "Starting lighttpd") or die();
$t->{REQUEST} = ( "GET /index.html HTTP/1.0\r\nHost: www.example.org\r\n" );
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => "/redirect" } );
ok($tf->handle_http($t) == 0, 'basic test');
my $myvar = "good";
my $server_name = "test.example.org";
my $mystr = "string";
$mystr .= "_append";
my $tests = {
"include" => "/good_include",
"concat" => "/good_" . "concat",
"servername1" => "/good_" . $server_name,
"servername2" => $server_name . "/good_",
"servername3" => "/good_" . $server_name . "/",
"var.myvar" => "/good_var_myvar" . $myvar,
"myvar" => "/good_myvar" . $myvar,
"number1" => "/good_number" . "1",
"number2" => "1" . "/good_number",
"array_append" => "/good_array_append",
"string_append" => "/good_" . $mystr,
"number_append" => "/good_" . "2"
};
foreach my $test (keys %{ $tests }) {
my $expect = $tests->{$test};
$t->{REQUEST} = ( "GET /$test HTTP/1.0\r\nHost: $server_name\r\n" );
$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => $expect } );
ok($tf->handle_http == 0, $test);
}
ok($tf->stop_proc == 0, "Stopping lighttpd");

2
tests/docroot/www/Makefile.am

@ -1,4 +1,4 @@
EXTRA_DIST=cgi.php cgi.pl dummydir index.html index.txt phpinfo.php \
phpself.php redirect.php cgi-pathinfo.pl phphost.php \
phpself.php redirect.php cgi-pathinfo.pl phphost.php pathinfo.php \
nph-status.pl prefix.fcgi
SUBDIRS=go indexfile expire

1
tests/prepare.sh

@ -32,6 +32,7 @@ cp $srcdir/docroot/123/*.txt \
$srcdir/docroot/123/*.php \
$srcdir/docroot/123/*.bla $tmpdir/servers/123.example.org/pages/
cp $srcdir/lighttpd.user $tmpdir/
cp $srcdir/var-include-sub.conf /tmp/
printf "%-40s" "preparing infrastructure"

24
tests/var-include-sub.conf

@ -0,0 +1,24 @@
# file to be included
$HTTP["host"] =~ "^" + server.name + "$" {
url.redirect = (
"^/include$" => "/good_include",
"^/concat$" => "/good_" + "concat",
"^/servername1$" => "/good_" + server.name,
"^/servername2$" => server.name + "/good_",
"^/servername3$" => "/good_" + server.name + "/",
"^/var.myvar$" => "/good_var_myvar" + var.myvar,
"^/myvar$" => "/good_myvar" + myvar,
"^/number1$" => "/good_number" + one,
"^/number2$" => one + "/good_number",
)
# without var prefix
mystr = "string"
mystr += "_append"
# from parent
one += 1
url.redirect += (
"^/array_append$" => "/good_array_append",
"^/string_append$" => "/good_" + mystr,
"^/number_append$" => "/good_" + one,
)
}

41
tests/var-include.conf

@ -0,0 +1,41 @@
debug.log-request-handling = "enable"
debug.log-condition-handling = "enable"
server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/"
server.pid-file = "/tmp/lighttpd/lighttpd.pid"
## bind to port (default: 80)
server.port = 2048
## bind to localhost (default: all interfaces)
server.bind = "localhost"
server.errorlog = "/tmp/lighttpd/logs/lighttpd.error.log"
server.name = "www.example.org"
server.tag = "Apache 1.3.29"
server.modules = ( "mod_redirect",
"mod_accesslog" )
######################## MODULE CONFIG ############################
accesslog.filename = "/tmp/lighttpd/logs/lighttpd.access.log"
mimetype.assign = ( ".html" => "text/html" )
url.redirect = ("^" => "/default")
$HTTP["host"] == "www.example.org" {
server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/"
server.name = "www.example.org"
url.redirect = ("^" => "/redirect")
}
$HTTP["host"] == "test.example.org" {
server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/"
server.name = "test.example.org"
var.myvar = "good"
var.one = 1
include "var-include-sub.conf"
}
Loading…
Cancel
Save