Browse Source

added include_shell option to configfiles (merged the rest of the trunk changesets)

git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-merge-1.4.x@530 152afb58-edef-0310-8abb-c4023f1b3aa9
svn/tags/lighttpd-1.4.2
Jan Kneschke 17 years ago
parent
commit
75c3a8393a
  1. 138
      configure.in
  2. 4
      doc/configuration.txt
  3. 5
      doc/lighttpd.conf
  4. 2
      src/.cvsignore
  5. 14
      src/Makefile.am
  6. 1
      src/array.h
  7. 11
      src/base.h
  8. 94
      src/configfile-glue.c
  9. 135
      src/configfile.c
  10. 1
      src/configfile.h
  11. 43
      src/configparser.y
  12. 18
      src/connections.c
  13. 11
      src/data_config.c
  14. 20
      src/keyvalue.c
  15. 3
      src/keyvalue.h
  16. 26
      src/mod_alias.c
  17. 28
      src/mod_redirect.c
  18. 13
      src/mod_rewrite.c
  19. 2
      src/plugin.c
  20. 1
      src/plugin.h
  21. 386
      src/proc_open.c
  22. 25
      src/proc_open.h
  23. 5
      src/response.c
  24. 1
      src/stream.c
  25. 8
      tests/LightyTest.pm
  26. 33
      tests/core-var-include.t
  27. 3
      tests/docroot/www/pathinfo.php
  28. 23
      tests/lighttpd.conf
  29. 10
      tests/mod-fastcgi.t
  30. 27
      tests/mod-redirect.t
  31. 6
      tests/var-include-sub.conf

138
configure.in

@ -96,7 +96,7 @@ AC_ARG_WITH(mysql,
if test \! -x $withval; then
echo "--with-mysql=path-to-mysql_config"
fi
if $withval | grep -- '--include' ; then
if $withval | grep -- '--include' > /dev/null ; then
MYSQL_INCLUDE="`$withval --include | sed s/\'//g`"
else
MYSQL_INCLUDE="`$withval --cflags | sed s/\'//g`"
@ -364,6 +364,9 @@ AC_ARG_ENABLE(lfs,
esac],[CPPFLAGS="${CPPFLAGS} -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES"])
AC_MSG_RESULT($enableval)
AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(off_t)
if test "x$ac_cv_func_sendfile" = xyes; then
# check if sendfile works
AC_MSG_CHECKING(if sendfile works)
@ -409,6 +412,7 @@ if test x$ipv6 = xtrue; then
fi
fi
AM_CONDITIONAL(CROSS_COMPILING, test "x$cross_compiling" = xyes)
dnl check for fastcgi lib, for the tests only
@ -440,51 +444,121 @@ AC_CONFIG_FILES([Makefile debian/Makefile src/Makefile doc/Makefile tests/Makefi
openwrt/Makefile openwrt/control openwrt/lighttpd.mk])
AC_OUTPUT
$ECHO
$ECHO "Plugins:"
$ECHO
do_build="mod_cgi mod_fastcgi mod_proxy mod_evhost mod_simple_vhost mod_access mod_alias mod_setenv mod_usertrack mod_auth mod_status mod_accesslog mod_rrdtool mod_secdownload mod_expire mod_compress mod_dirlisting mod_indexfiles mod_userdir"
plugins="mod_rewrite mod_redirect mod_ssi"
features="regex-conditionals"
if test ! "x$PCRE_LIB" = x; then
$ECHO "mod_rewrite : enabled"
$ECHO "mod_redirect : enabled"
$ECHO "mod_ssi : enabled"
do_build="$do_build $plugins"
enable_feature="$features"
else
no_build="$no_build $plugins"
disable_feature="$features"
fi
plugins="mod_mysql_vhost"
if test ! "x$MYSQL_LIBS" = x; then
do_build="$do_build $plugins"
else
$ECHO "mod_rewrite : disabled (libpcre missing)"
$ECHO "mod_redirect : disabled (libpcre missing)"
$ECHO "mod_ssi : disabled (libpcre missing)"
no_build="$no_build $plugins"
fi
$ECHO "mod_cgi : enabled"
$ECHO "mod_fastcgi : enabled"
$ECHO "mod_proxy : enabled"
$ECHO "mod_evhost : enabled"
$ECHO "mod_simple_vhost: enabled"
plugins="mod_trigger_b4_dl"
if test ! "x$GDBM_LIB" = x; then
do_build="$do_build $plugins"
else
no_build="$no_build $plugins"
fi
features="compress-gzip compress-deflate"
if test ! "x$Z_LIB" = x; then
enable_feature="$enable_feature $features"
else
disable_feature="$disable_feature $features"
fi
features="compress-bzip2"
if test ! "x$BZ_LIB" = x; then
enable_feature="$enable_feature $features"
else
disable_feature="$disable_feature $features"
fi
if test "x$MYSQL_LIBS" = x; then
$ECHO "mod_mysql_vhost : disabled (libmysqlclient missing or mysql support disabled)"
features="auth-ldap"
if test ! "x$LDAP_LIB" = x; then
enable_feature="$enable_feature $features"
else
$ECHO "mod_mysql_vhost : enabled"
disable_feature="$disable_feature $features"
fi
$ECHO "mod_access : enabled"
$ECHO "mod_alias : enabled"
$ECHO "mod_setenv : enabled"
$ECHO "mod_usertrack : enabled"
if test "x$Z_LIB" = x; then
$ECHO "mod_compress : disabled (libz missing)"
features="network-openssl"
if test ! "x$SSL_LIB" = x; then
enable_feature="$enable_feature $features"
else
$ECHO "mod_compress : enabled"
disable_feature="$disable_feature $features"
fi
# no crypt call
features="auth-crypt"
if test "$ac_cv_search_crypt" = no; then
$ECHO "mod_auth : enabled, crypt() support disabled"
disable_feature="$disable_feature $features"
else
$ECHO "mod_auth : enabled"
enable_feature="$enable_feature $features"
fi
$ECHO "mod_status : enabled"
$ECHO "mod_accesslog : enabled"
$ECHO "mod_rrdtool : enabled"
$ECHO "mod_secdownload : enabled"
$ECHO "mod_expire : enabled"
features="network-ipv6"
if test "$ac_cv_ipv6_support" = yes; then
enable_feature="$enable_feature $features"
else
disable_feature="$disable_feature $features"
fi
features="large-files"
if test "$enable_lfs" = yes; then
enable_feature="$enable_feature $features"
else
disable_feature="$disable_feature $features"
fi
## post processing
do_build=`echo $do_build | sed 's/ /\n/g' | sort`
no_build=`echo $no_build | sed 's/ /\n/g' | sort`
enable_feature=`echo $enable_feature | sed 's/ /\n/g' | sort`
disable_feature=`echo $disable_feature | sed 's/ /\n/g' | sort`
## output
$ECHO
$ECHO "Plugins:"
$ECHO
$ECHO "enabled: "
i=0
for p in $do_build; do
$ECHO " $p"
done
$ECHO "disabled: "
for p in $no_build; do
$ECHO " $p"
done
$ECHO
$ECHO "Features:"
$ECHO
$ECHO "enabled: "
i=0
p=""
for p in $enable_feature; do
$ECHO " $p"
done
$ECHO "disabled: "
for p in $disable_feature; do
$ECHO " $p"
done
$ECHO

4
doc/configuration.txt

@ -35,6 +35,7 @@ A BNF like notation: ::
<boolean>: ( "enable" | "disable" )
<array> : "(" [ <string> "=>" ] <value> [, [ <string> "=>" ] <value> ]* ")"
INCLUDE : "include" VALUE
INCLUDE_SHELL : "include_shell" STRING_VALUE
Example
-------
@ -60,6 +61,9 @@ Example
# include, relative to dirname of main config file
include "mime.types.conf"
# read configuration from output of a command
include_shell "/usr/local/bin/confmimetype /etc/mime.types"
Conditional Configuration
=========================

5
doc/lighttpd.conf

@ -301,3 +301,8 @@ $HTTP["url"] =~ "\.pdf$" {
#include /etc/lighttpd/lighttpd-inc.conf
## same as above if you run: "lighttpd -f /etc/lighttpd/lighttpd.conf"
#include "lighttpd-inc.conf"
#### include_shell
#include_shell "echo var.a=1"
## the above is same as:
#var.a=1

2
src/.cvsignore

@ -9,6 +9,8 @@ lighttpd
.deps
.libs
array
proc_open
regex
mod_ssi_exprparser.c
mod_ssi_exprparser.h
configparser.c

14
src/Makefile.am

@ -1,4 +1,4 @@
noinst_PROGRAMS=array chunk lemon # simple-fcgi #graphic evalo bench ajp ssl error_test adserver gen-license
noinst_PROGRAMS=proc_open lemon # simple-fcgi #graphic evalo bench ajp ssl error_test adserver gen-license
sbin_PROGRAMS=lighttpd
bin_PROGRAMS=spawn-fcgi
LEMON=$(top_builddir)/src/lemon
@ -47,8 +47,7 @@ src = server.c response.c connections.c network.c \
network_write.c network_linux_sendfile.c \
network_freebsd_sendfile.c network_writev.c \
network_solaris_sendfilev.c network_openssl.c \
configfile.c request.c
configfile.c request.c proc_open.c
spawn_fcgi_SOURCES=spawn-fcgi.c
@ -208,7 +207,7 @@ hdr = server.h buffer.h network.h log.h keyvalue.h \
mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \
configparser.h mod_ssi_exprparser.h \
sys-mmap.h sys-socket.h mod_cml.h mod_cml_funcs.h \
splaytree.h
splaytree.h proc_open.h
DEFS= @DEFS@ -DLIBRARY_DIR="\"$(libdir)\""
@ -217,11 +216,8 @@ lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_liba
lighttpd_LDFLAGS = -export-dynamic
lighttpd_CCPFLAGS = $(FAM_CFLAGS)
array_SOURCES = array.c buffer.c data_string.c data_count.c
array_CPPFLAGS= -DDEBUG_ARRAY
chunk_SOURCES = buffer.c chunk.c
chunk_CPPFLAGS= -DDEBUG_CHUNK
proc_open_SOURCES = proc_open.c buffer.c
proc_open_CPPFLAGS= -DDEBUG_PROC_OPEN
#gen_license_SOURCES = license.c md5.c buffer.c gen_license.c

1
src/array.h

@ -64,7 +64,6 @@ typedef struct {
data_array *data_array_init(void);
typedef enum { CONFIG_COND_UNSET, CONFIG_COND_EQ, CONFIG_COND_MATCH, CONFIG_COND_NE, CONFIG_COND_NOMATCH } config_cond_t;
typedef enum { COND_RESULT_FALSE, COND_RESULT_TRUE, COND_RESULT_UNSET } cond_result_t;
#define PATCHES NULL, "SERVERsocket", "HTTPurl", "HTTPhost", "HTTPreferer", "HTTPuseragent", "HTTPcookie", "HTTPremoteip"
typedef enum {

11
src/base.h

@ -290,6 +290,14 @@ typedef struct {
typedef enum { CON_STATE_CONNECT, CON_STATE_REQUEST_START, CON_STATE_READ, CON_STATE_REQUEST_END, CON_STATE_READ_POST, CON_STATE_HANDLE_REQUEST, CON_STATE_RESPONSE_START, CON_STATE_WRITE, CON_STATE_RESPONSE_END, CON_STATE_ERROR, CON_STATE_CLOSE } connection_state_t;
typedef enum { COND_RESULT_UNSET, COND_RESULT_FALSE, COND_RESULT_TRUE } cond_result_t;
typedef struct {
cond_result_t result;
int patterncount;
int matches[3 * 10];
buffer *comp_value; /* just a pointer */
} cond_cache_t;
typedef struct {
connection_state_t state;
@ -336,6 +344,7 @@ typedef struct {
int http_status;
sock_addr dst_addr;
buffer *dst_addr_buf;
/* request */
buffer *parse_request;
@ -361,7 +370,7 @@ typedef struct {
void **plugin_ctx; /* plugin connection specific config */
specific_config conf; /* global connection specific config */
cond_result_t *cond_results_cache;
cond_cache_t *cond_cache;
buffer *server_name;

94
src/configfile-glue.c

@ -147,7 +147,7 @@ int config_insert_values_global(server *srv, array *ca, const config_values_t cv
return config_insert_values_internal(srv, ca, cv);
}
static int config_check_cond_cached(server *srv, connection *con, data_config *dc);
static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc);
static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) {
buffer *l;
@ -155,21 +155,21 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat
/* check parent first */
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);
log_error_write(srv, __FILE__, __LINE__, "sb", "go parent", dc->parent->key);
}
if (!config_check_cond_cached(srv, con, dc->parent)) {
if (config_check_cond_cached(srv, con, dc->parent) == COND_RESULT_FALSE) {
return COND_RESULT_FALSE;
}
}
if (dc->prev) {
if (con->conf.log_condition_handling) {
log_error_write(srv, __FILE__, __LINE__, "sb", "go prev", dc->prev->string);
log_error_write(srv, __FILE__, __LINE__, "sb", "go prev", dc->prev->key);
}
/* make sure prev is checked first */
config_check_cond_cached(srv, con, dc->prev);
/* one of prev set me to FALSE */
if (con->cond_results_cache[dc->context_ndx] == COND_RESULT_FALSE) {
if (COND_RESULT_FALSE == con->cond_cache[dc->context_ndx].result) {
return COND_RESULT_FALSE;
}
}
@ -178,7 +178,8 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat
l = srv->empty_string;
if (COMP_HTTP_HOST == dc->comp) {
switch (dc->comp) {
case COMP_HTTP_HOST: {
l = con->uri.authority;
#if 0
/* FIXME: get this working again */
@ -208,7 +209,9 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat
break;
}
}
} else if (COMP_HTTP_REMOTEIP == dc->comp) {
break;
}
case COMP_HTTP_REMOTEIP: {
char *nm_slash;
/* handle remoteip limitations
*
@ -283,27 +286,40 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat
buffer_copy_string(srv->cond_check_buf, s);
}
#endif
} else if (COMP_HTTP_URL == dc->comp) {
break;
}
case COMP_HTTP_URL:
l = con->uri.path;
} else if (COMP_SERVER_SOCKET == dc->comp) {
break;
case COMP_SERVER_SOCKET:
l = srv_sock->srv_token;
} else if (COMP_HTTP_REFERER == dc->comp) {
break;
case COMP_HTTP_REFERER: {
data_string *ds;
if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) {
l = ds->value;
}
} else if (COMP_HTTP_COOKIE == dc->comp) {
break;
}
case COMP_HTTP_COOKIE: {
data_string *ds;
if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
l = ds->value;
}
} else if (COMP_HTTP_USERAGENT == dc->comp) {
break;
}
case COMP_HTTP_USERAGENT: {
data_string *ds;
if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) {
l = ds->value;
}
} else {
break;
}
default:
return COND_RESULT_FALSE;
}
@ -322,15 +338,21 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat
#ifdef HAVE_PCRE_H
case CONFIG_COND_NOMATCH:
case CONFIG_COND_MATCH: {
#define N 10
int ovec[N * 3];
cond_cache_t *cache = &con->cond_cache[dc->context_ndx];
int n;
n = pcre_exec(dc->regex, dc->regex_study, l->ptr, l->used - 1, 0, 0, ovec, N * 3);
#ifndef elementsof
#define elementsof(x) (sizeof(x) / sizeof(x[0]))
#endif
n = pcre_exec(dc->regex, dc->regex_study, l->ptr, l->used - 1, 0, 0,
cache->matches, elementsof(cache->matches));
if (n > 0) {
cache->patterncount = n;
cache->comp_value = l;
return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
} else {
/* cache is already cleared */
return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
}
break;
@ -344,36 +366,56 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat
return COND_RESULT_FALSE;
}
static int config_check_cond_cached(server *srv, connection *con, data_config *dc) {
cond_result_t *cache = con->cond_results_cache;
static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc) {
cond_cache_t *caches = con->cond_cache;
if (cache[dc->context_ndx] == COND_RESULT_UNSET) {
if (COND_RESULT_TRUE == (cache[dc->context_ndx] = config_check_cond_nocache(srv, con, dc))) {
if (COND_RESULT_UNSET == caches[dc->context_ndx].result) {
if (COND_RESULT_TRUE == (caches[dc->context_ndx].result = config_check_cond_nocache(srv, con, dc))) {
if (dc->next) {
data_config *c;
if (con->conf.log_condition_handling) {
log_error_write(srv, __FILE__, __LINE__, "s", "setting remains of chaining to FALSE");
log_error_write(srv, __FILE__, __LINE__, "s",
"setting remains of chaining to false");
}
for (c = dc->next; c; c = c->next) {
cache[c->context_ndx] = COND_RESULT_FALSE;
caches[c->context_ndx].result = COND_RESULT_FALSE;
}
}
}
if (con->conf.log_condition_handling) {
log_error_write(srv, __FILE__, __LINE__, "dsd", dc->context_ndx, "(uncached) result:", cache[dc->context_ndx]);
log_error_write(srv, __FILE__, __LINE__, "dss", dc->context_ndx,
"(uncached) result:",
caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false");
}
}
else {
if (con->conf.log_condition_handling) {
log_error_write(srv, __FILE__, __LINE__, "dsd", dc->context_ndx, "(cached) result:", cache[dc->context_ndx]);
log_error_write(srv, __FILE__, __LINE__, "dss", dc->context_ndx,
"(cached) result:",
caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false");
}
}
return cache[dc->context_ndx];
return caches[dc->context_ndx].result;
}
int config_check_cond(server *srv, connection *con, data_config *dc) {
if (con->conf.log_condition_handling) {
log_error_write(srv, __FILE__, __LINE__, "s", "=== start of condition block ===");
}
return config_check_cond_cached(srv, con, dc);
return (config_check_cond_cached(srv, con, dc) == COND_RESULT_TRUE);
}
int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n)
{
cond_cache_t *cache = &con->cond_cache[dc->context_ndx];
if (n > cache->patterncount) {
return 0;
}
n <<= 1; /* n *= 2 */
buffer_append_string_len(buf,
cache->comp_value->ptr + cache->matches[n],
cache->matches[n + 1] - cache->matches[n]);
return 1;
}

135
src/configfile.c

@ -19,6 +19,7 @@
#include "configparser.h"
#include "configfile.h"
#include "proc_open.h"
static int config_insert(server *srv) {
@ -225,11 +226,6 @@ static int config_insert(server *srv) {
#define PATCH(x) con->conf.x = s->x
int config_setup_connection(server *srv, connection *con) {
specific_config *s = srv->config_storage[0];
int i;
for (i = srv->config_context->used - 1; i >= 0; i --) {
con->cond_results_cache[i] = COND_RESULT_UNSET;
}
PATCH(allow_http11);
PATCH(mimetypes);
@ -351,12 +347,12 @@ int config_patch_connection(server *srv, connection *con, comp_key_t comp) {
return 0;
}
#undef PATCH
typedef struct {
int foo;
int bar;
buffer *file;
stream s;
const buffer *source;
const char *input;
size_t offset;
size_t size;
@ -369,6 +365,7 @@ typedef struct {
int in_cond;
} tokenizer_t;
#if 0
static int tokenizer_open(server *srv, tokenizer_t *t, buffer *basedir, const char *fn) {
if (buffer_is_empty(basedir) &&
(fn[0] == '/' || fn[0] == '\\') &&
@ -404,7 +401,7 @@ static int tokenizer_close(server *srv, tokenizer_t *t) {
buffer_free(t->file);
return stream_close(&(t->s));
}
#endif
static int config_skip_newline(tokenizer_t *t) {
int skipped = 1;
assert(t->input[t->offset] == '\r' || t->input[t->offset] == '\n');
@ -445,7 +442,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
tid = TK_ARRAY_ASSIGN;
} else {
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"use => for assignments in arrays");
return -1;
@ -465,7 +462,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
tid = TK_MATCH;
} else {
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"only =~ and == are allow in the condition");
return -1;
@ -481,7 +478,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
t->line_pos++;
} else {
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"unexpected equal-sign: =");
return -1;
@ -504,7 +501,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
tid = TK_NOMATCH;
} else {
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"only !~ and != are allow in the condition");
return -1;
@ -513,7 +510,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
t->in_cond = 0;
} else {
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"unexpected exclamation-marks: !");
return -1;
@ -603,7 +600,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
/* ERROR */
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"missing closing quote");
@ -646,8 +643,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
t->offset += 2;
buffer_copy_string(token, "+=");
tid = TK_APPEND;
}
else {
} else {
t->offset++;
tid = TK_PLUS;
buffer_copy_string(token, "+");
@ -708,7 +704,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
} else {
/* ERROR */
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"invalid character in condition");
return -1;
@ -728,7 +724,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
} else {
/* ERROR */
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"unexpected EOF");
@ -748,6 +744,8 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
if (strcmp(token->ptr, "include") == 0) {
tid = TK_INCLUDE;
} else if (strcmp(token->ptr, "include_shell") == 0) {
tid = TK_INCLUDE_SHELL;
} else if (strcmp(token->ptr, "else") == 0) {
tid = TK_ELSE;
} else {
@ -759,7 +757,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
} else {
/* ERROR */
log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"invalid character in variable name");
return -1;
@ -773,7 +771,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
*token_id = tid;
#if 0
log_error_write(srv, __FILE__, __LINE__, "sbsdsdbdd",
"file:", t->file,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
token, token->used - 1, tid);
#endif
@ -787,21 +785,16 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *
return 0;
}
int config_parse_file(server *srv, config_t *context, const char *fn) {
tokenizer_t t;
static int config_parse(server *srv, config_t *context, tokenizer_t *t) {
void *pParser;
int token_id;
buffer *token, *lasttoken;
int ret;
if (tokenizer_open(srv, &t, context->basedir, fn) == -1) {
return -1;
}
pParser = configparserAlloc( malloc );
lasttoken = buffer_init();
token = buffer_init();
while((1 == (ret = config_tokenizer(srv, &t, &token_id, token))) && context->ok) {
while((1 == (ret = config_tokenizer(srv, t, &token_id, token))) && context->ok) {
buffer_copy_string_buffer(lasttoken, token);
configparser(pParser, token_id, token, context);
@ -821,20 +814,98 @@ int config_parse_file(server *srv, config_t *context, const char *fn) {
if (ret == -1) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"configfile parser failed:", lasttoken);
}
else if (context->ok == 0) {
} else if (context->ok == 0) {
log_error_write(srv, __FILE__, __LINE__, "sbsdsdsb",
"file:", t.file,
"line:", t.line, "pos:", t.line_pos,
"source:", t->source,
"line:", t->line, "pos:", t->line_pos,
"parser failed somehow near here:", lasttoken);
ret = -1;
}
buffer_free(lasttoken);
tokenizer_close(srv, &t);
return ret == -1 ? -1 : 0;
}
static int tokenizer_init(tokenizer_t *t, const buffer *source, const char *input, size_t size) {
t->source = source;
t->input = input;
t->size = size;
t->offset = 0;
t->line = 1;
t->line_pos = 1;
t->in_key = 1;
t->in_brace = 0;
t->in_cond = 0;
return 0;
}
int config_parse_file(server *srv, config_t *context, const char *fn) {
tokenizer_t t;
stream s;
int ret;
buffer *filename;
if (buffer_is_empty(context->basedir) &&
(fn[0] == '/' || fn[0] == '\\') &&
(fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) {
filename = buffer_init_string(fn);
} else {
filename = buffer_init_buffer(context->basedir);
buffer_append_string(filename, fn);
}
if (0 != stream_open(&s, filename)) {
log_error_write(srv, __FILE__, __LINE__, "sbss",
"opening configfile ", filename, "failed:", strerror(errno));
ret = -1;
} else {
tokenizer_init(&t, filename, s.start, s.size);
ret = config_parse(srv, context, &t);
}
stream_close(&s);
buffer_free(filename);
return ret;
}
int config_parse_cmd(server *srv, config_t *context, const char *cmd) {
proc_handler_t proc;
tokenizer_t t;
int ret;
buffer *source;
buffer *out;
char oldpwd[PATH_MAX];
if (NULL == getcwd(oldpwd, sizeof(oldpwd))) {
log_error_write(srv, __FILE__, __LINE__, "s",
"cannot get cwd", strerror(errno));
return -1;
}
source = buffer_init_string(cmd);
out = buffer_init();
if (!buffer_is_empty(context->basedir)) {
chdir(context->basedir->ptr);
}
if (0 != proc_open_buffer(&proc, cmd, NULL, out, NULL)) {
log_error_write(srv, __FILE__, __LINE__, "sbss",
"opening", source, "failed:", strerror(errno));
ret = -1;
} else {
tokenizer_init(&t, source, out->ptr, out->used);
ret = config_parse(srv, context, &t);
}
buffer_free(source);
buffer_free(out);
chdir(oldpwd);
return ret;
}
static void context_init(server *srv, config_t *context) {
context->srv = srv;
context->ok = 1;

1
src/configfile.h

@ -18,5 +18,6 @@ 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);
int config_parse_cmd(server *srv, config_t *context, const char *cmd);
#endif

43
src/configparser.y

@ -131,6 +131,7 @@ metalines ::= .
metaline ::= varline.
metaline ::= condlines(A) EOL. { A = NULL; }
metaline ::= include.
metaline ::= include_shell.
metaline ::= EOL.
%type value {data_unset *}
@ -141,17 +142,17 @@ metaline ::= EOL.
%type aelements {array *}
%type array {array *}
%type key {buffer *}
%type stringop {buffer *}
%type cond {config_cond_t }
%destructor value { $$->free($$); }
%destructor expression { $$->free($$); }
%destructor aelement { $$->free($$); }
%destructor condline { $$->free((data_unset *)$$); }
%destructor condlines { $$->free((data_unset *)$$); }
%destructor aelements { array_free($$); }
%destructor array { array_free($$); }
%destructor key { buffer_free($$); }
%destructor stringop { buffer_free($$); }
%token_type {buffer *}
%token_destructor { buffer_free($$); }
@ -347,7 +348,7 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) expression(
break;
default:
assert(0);
break;
return;
}
b = buffer_init();
@ -470,18 +471,36 @@ cond(A) ::= NOMATCH. {
A = CONFIG_COND_NOMATCH;
}
include ::= INCLUDE expression(A). {
stringop(A) ::= expression(B). {
A = NULL;
if (ctx->ok) {
if (A->type != TYPE_STRING) {
fprintf(stderr, "file must be string");
if (B->type != TYPE_STRING) {
fprintf(stderr, "operand 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 = buffer_init_buffer(((data_string*)B)->value);
}
}
A->free(A);
A = NULL;
B->free(B);
B = NULL;
}
include ::= INCLUDE stringop(A). {
if (ctx->ok) {
if (0 != config_parse_file(ctx->srv, ctx, A->ptr)) {
ctx->ok = 0;
}
buffer_free(A);
A = NULL;
}
}
include_shell ::= INCLUDE_SHELL stringop(A). {
if (ctx->ok) {
if (0 != config_parse_cmd(ctx->srv, ctx, A->ptr)) {
ctx->ok = 0;
}
buffer_free(A);
A = NULL;
}
}

18
src/connections.c

@ -23,6 +23,8 @@
#include "plugin.h"
#include "inet_ntop_cache.h"
#ifdef USE_OPENSSL
# include <openssl/ssl.h>
# include <openssl/err.h>
@ -479,6 +481,7 @@ static int connection_handle_write_prepare(server *srv, connection *con) {
break;
}
case 207:
break;
default:
if (con->request.http_method == HTTP_METHOD_HEAD ||
@ -603,7 +606,8 @@ connection *connection_init(server *srv) {
con->plugin_ctx = calloc(srv->plugins.used + 1, sizeof(void *));
con->cond_results_cache = calloc(srv->config_context->used, sizeof(cond_result_t));
con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t));
con->dst_addr_buf = buffer_init();
config_setup_connection(srv, con);
return con;
@ -653,7 +657,7 @@ void connections_free(server *srv) {
CLEAN(error_handler);
#undef CLEAN
free(con->plugin_ctx);
free(con->cond_results_cache);
free(con->cond_cache);
free(con);
}
@ -753,6 +757,15 @@ int connection_reset(server *srv, connection *con) {
con->plugin_ctx[i] = NULL;
}
#if COND_RESULT_UNSET
for (i = srv->config_context->used - 1; i >= 0; i --) {
con->cond_cache[i].result = COND_RESULT_UNSET;
con->cond_cache[i].patterncount = 0;
}
#else
memset(con->cond_cache, 0, sizeof(cond_cache_t) * srv->config_context->used);
#endif
con->header_len = 0;
con->in_error_handler = 0;
@ -1132,6 +1145,7 @@ connection *connection_accept(server *srv, server_socket *srv_socket) {
con->connection_start = srv->cur_ts;
con->dst_addr = cnt_addr;
buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
con->srv_socket = srv_socket;
if (-1 == (fdevent_fcntl_set(srv->ev, con->fd))) {

11
src/data_config.c

@ -73,10 +73,13 @@ static void data_config_print(const data_unset *d, int depth) {
for (i = 0; i < ds->childs->used; i ++) {
data_unset *du = ds->childs->data[i];
fprintf(stderr, "\n");
array_print_indent(depth + 1);
du->print(du, depth + 1);
fprintf(stderr, "\n");
/* only the 1st block of chaining */
if (NULL == ((data_config *)du)->prev) {
fprintf(stderr, "\n");
array_print_indent(depth + 1);
du->print(du, depth + 1);
fprintf(stderr, "\n");
}
}
}

20
src/keyvalue.c

@ -292,6 +292,7 @@ int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, cons
size_t i;
const char *errptr;
int erroff;
pcre_keyvalue *kv;
#endif
if (!key) return -1;
@ -316,14 +317,20 @@ int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, cons
}
}
if (NULL == (kvb->kv[kvb->used]->key = pcre_compile(key,
kv = kvb->kv[kvb->used];
if (NULL == (kv->key = pcre_compile(key,
0, &errptr, &erroff, NULL))) {
fprintf(stderr, "%s.%d: rexexp compilation error at %s\n", __FILE__, __LINE__, errptr);
return -1;
}
if (NULL == (kv->key_extra = pcre_study(kv->key, 0, &errptr)) &&
errptr != NULL) {
return -1;
}
kvb->kv[kvb->used]->value = strdup(value);
kv->value = buffer_init_string(value);
kvb->used++;
@ -339,11 +346,14 @@ int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, cons
void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb) {
#ifdef HAVE_PCRE_H
size_t i;
pcre_keyvalue *kv;
for (i = 0; i < kvb->size; i++) {
if (kvb->kv[i]->key) pcre_free(kvb->kv[i]->key);
if (kvb->kv[i]->value) free(kvb->kv[i]->value);
free(kvb->kv[i]);
kv = kvb->kv[i];
if (kv->key) pcre_free(kv->key);
if (kv->key_extra) pcre_free(kv->key_extra);
if (kv->value) buffer_free(kv->value);
free(kv);
}
if (kvb->kv) free(kvb->kv);

3
src/keyvalue.h

@ -25,9 +25,10 @@ typedef struct {
typedef struct {
#ifdef HAVE_PCRE_H
pcre *key;
pcre_extra *key_extra;
#endif
char *value;
buffer *value;
} pcre_keyvalue;
typedef enum { HTTP_AUTH_BASIC, HTTP_AUTH_DIGEST } httpauth_type;

26
src/mod_alias.c

@ -1,6 +1,7 @@
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "base.h"
#include "log.h"
@ -86,6 +87,31 @@ SETDEFAULTS_FUNC(mod_alias_set_defaults) {
if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
return HANDLER_ERROR;
}
if (s->alias->used >= 2) {
const array *a = s->alias;
size_t j, k;
for (j = 0; j < a->used; j ++) {
const buffer *prefix = a->data[a->sorted[j]]->key;
for (k = j + 1; k < a->used; k ++) {
const buffer *key = a->data[a->sorted[k]]->key;
if (key->used < prefix->used) {
break;
}
if (memcmp(key->ptr, prefix->ptr, prefix->used - 1) != 0) {
break;
}
/* ok, they have same prefix. check position */
if (a->sorted[j] < a->sorted[k]) {
fprintf(stderr, "url.alias: `%s' will never match as `%s' matched first\n",
key->ptr,
prefix->ptr);
return HANDLER_ERROR;
}
}
}
}
}
return HANDLER_GO_ON;

28
src/mod_redirect.c

@ -15,6 +15,7 @@
typedef struct {
pcre_keyvalue_buffer *redirect;
data_config *context; /* to which apply me */
} plugin_config;
typedef struct {
@ -165,6 +166,7 @@ static int mod_redirect_patch_connection(server *srv, connection *con, plugin_da
if (0 == strcmp(du->key->ptr, "url.redirect")) {
p->conf.redirect = s->redirect;
p->conf.context = dc;
}
}
}
@ -190,19 +192,22 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_
for (i = 0; i < p->conf.redirect->used; i++) {
pcre *match;
pcre_extra *extra;
const char *pattern;
size_t pattern_len;
int n;
pcre_keyvalue *kv = p->conf.redirect->kv[i];
# define N 10
int ovec[N * 3];
match = p->conf.redirect->kv[i]->key;
pattern = p->conf.redirect->kv[i]->value;
pattern_len = strlen(pattern);
match = kv->key;
extra = kv->key_extra;
pattern = kv->value->ptr;
pattern_len = kv->value->used - 1;
if ((n = pcre_exec(match, NULL, p->match_buf->ptr, p->match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) {
if ((n = pcre_exec(match, extra, p->match_buf->ptr, p->match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) {
if (n != PCRE_ERROR_NOMATCH) {
log_error_write(srv, __FILE__, __LINE__, "sd"
log_error_write(srv, __FILE__, __LINE__, "sd",
"execution error while matching: ", n);
return HANDLER_ERROR;
}
@ -210,6 +215,7 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_
const char **list;
size_t start, end;
size_t k;
/* it matched */
pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list);
@ -219,7 +225,7 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_
start = 0; end = pattern_len;
for (k = 0; k < pattern_len; k++) {
if (pattern[k] == '$' &&
if ((pattern[k] == '$' || pattern[k] == '%') &&
isdigit((unsigned char)pattern[k + 1])) {
/* got one */
@ -229,9 +235,13 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_
buffer_append_string_len(p->location, pattern + start, end - start);
/* n is always > 0 */
if (num < (size_t)n) {
buffer_append_string(p->location, list[num]);
if (pattern[k] == '$') {
/* n is always > 0 */
if (num < (size_t)n) {
buffer_append_string(p->location, list[num]);
}
} else {
config_append_cond_match_buffer(con, p->conf.context, p->location, num);
}
k++;

13
src/mod_rewrite.c

@ -31,6 +31,7 @@ typedef struct {
typedef struct {
rewrite_rule_buffer *rewrite;
data_config *context; /* to which apply me */
} plugin_config;
typedef struct {
@ -377,7 +378,7 @@ URIHANDLER_FUNC(mod_rewrite_uri_handler) {
start = 0; end = pattern_len;
for (k = 0; k < pattern_len; k++) {
if (pattern[k] == '$' &&
if ((pattern[k] == '$' || pattern[k] == '%') &&
isdigit((unsigned char)pattern[k + 1])) {
/* got one */
@ -387,9 +388,13 @@ URIHANDLER_FUNC(mod_rewrite_uri_handler) {
buffer_append_string_len(con->request.uri, pattern + start, end - start);
/* n is always larger than 0 */
if (num < (size_t)n) {
buffer_append_string(con->request.uri, list[num]);
if (pattern[k] == '$') {
/* n is always > 0 */
if (num < (size_t)n) {
buffer_append_string(con->request.uri, list[num]);
}
} else {
config_append_cond_match_buffer(con, p->conf.context, con->request.uri, num);
}
k++;

2
src/plugin.c

@ -62,7 +62,7 @@ static void plugin_free(plugin *p) {
int use_dlclose = 1;
if (p->name) buffer_free(p->name);
#ifdef HAVE_VALGRIND_VALGRIND_H
if (RUNNING_ON_VALGRIND) use_dlclose = 0;
/*if (RUNNING_ON_VALGRIND) use_dlclose = 0;*/
#endif
if (use_dlclose && p->lib) {

1
src/plugin.h

@ -89,5 +89,6 @@ int config_insert_values_internal(server *srv, array *ca, const config_values_t
int config_setup_connection(server *srv, connection *con);
int config_patch_connection(server *srv, connection *con, comp_key_t comp);
int config_check_cond(server *srv, connection *con, data_config *dc);
int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n);
#endif

386
src/proc_open.c

@ -0,0 +1,386 @@
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "proc_open.h"
#ifdef WIN32
#include <io.h>
#include <fcntl.h>
#else
#include <sys/wait.h>
#include <unistd.h>
#endif
#ifdef WIN32
/* {{{ win32 stuff */
# define SHELLENV "ComSpec"
# define SECURITY_DC , SECURITY_ATTRIBUTES *security
# define SECURITY_CC , security
# define pipe(pair) (CreatePipe(&pair[0], &pair[1], security, 2048L) ? 0 : -1)
static inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig)
{
HANDLE copy, self = GetCurrentProcess();
if (!DuplicateHandle(self, src, self, &copy, 0, inherit, DUPLICATE_SAME_ACCESS |
(closeorig ? DUPLICATE_CLOSE_SOURCE : 0)))
return NULL;
return copy;
}
# define close_descriptor(fd) CloseHandle(fd)
static void pipe_close_parent(pipe_t *p) {
/* don't let the child inherit the parent side of the pipe */
p->parent = dup_handle(p->parent, FALSE, TRUE);
}
static void pipe_close_child(pipe_t *p) {
close_descriptor(p->child);
p->fd = _open_osfhandle((long)p->parent,
(p->fd == 0 ? O_RDONLY : O_WRONLY)|O_BINARY);
}
/* }}} */
#else /* WIN32 */
/* {{{ unix way */
# define SHELLENV "SHELL"
# define SECURITY_DC
# define SECURITY_CC
# define close_descriptor(fd) close(fd)
static void pipe_close_parent(pipe_t *p) {
/* don't close stdin */
close_descriptor(p->parent);
if (dup2(p->child, p->fd) != p->fd) {
perror("pipe_child dup2");
} else {
close_descriptor(p->child);
p->child = p->fd;
}
}
static void pipe_close_child(pipe_t *p) {
close_descriptor(p->child);
p->fd = p->parent;
}
/* }}} */
#endif /* WIN32 */
/* {{{ pipe_close */
static void pipe_close(pipe_t *p) {
close_descriptor(p->parent);
close_descriptor(p->child);
#ifdef WIN32
close(p->fd);
#endif
}
/* }}} */
/* {{{ pipe_open */
static int pipe_open(pipe_t *p, int fd SECURITY_DC) {
descriptor_t newpipe[2];
if (0 != pipe(newpipe)) {
fprintf(stderr, "can't open pipe");
return -1;
}
if (0 == fd) {
p->parent = newpipe[1]; /* write */
p->child = newpipe[0]; /* read */
} else {
p->parent = newpipe[0]; /* read */
p->child = newpipe[1]; /* write */
}
p->fd = fd;
return 0;
}
/* }}} */
/* {{{ proc_open_pipes */
static int proc_open_pipes(proc_handler_t *proc SECURITY_DC) {
if (pipe_open(&(proc->in), 0 SECURITY_CC) != 0) {
return -1;
}
if (pipe_open(&(proc->out), 1 SECURITY_CC) != 0) {
return -1;
}
if (pipe_open(&(proc->err), 2 SECURITY_CC) != 0) {
return -1;
}
return 0;
}
/* }}} */
/* {{{ proc_close_pipes */
static void proc_close_pipes(proc_handler_t *proc) {
pipe_close(&proc->in);
pipe_close(&proc->out);
pipe_close(&proc->err);
}
/* }}} */
/* {{{ proc_close_parents */
static void proc_close_parents(proc_handler_t *proc) {
pipe_close_parent(&proc->in);
pipe_close_parent(&proc->out);
pipe_close_parent(&proc->err);
}
/* }}} */
/* {{{ proc_close_childs */
static void proc_close_childs(proc_handler_t *proc) {
pipe_close_child(&proc->in);
pipe_close_child(&proc->out);
pipe_close_child(&proc->err);
}
/* }}} */
#ifdef WIN32
/* {{{ proc_close */
int proc_close(proc_handler_t *proc) {
proc_pid_t child = proc->child;
DWORD wstatus;
proc_close_pipes(proc);
WaitForSingleObject(child, INFINITE);
GetExitCodeProcess(child, &wstatus);
CloseHandle(child);
return wstatus;
}
/* }}} */
/* {{{ proc_open */
int proc_open(proc_handler_t *proc, const char *command) {
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL procok;
SECURITY_ATTRIBUTES security;
const char *shell;
buffer *cmdline;
if (NULL == (shell = getenv(SHELLENV))) {
fprintf(stderr, "env %s is required", SHELLENV);
return -1;
}
/* we use this to allow the child to inherit handles */
memset(&security, 0, sizeof(security));
security.nLength = sizeof(security);
security.bInheritHandle = TRUE;
security.lpSecurityDescriptor = NULL;
if (proc_open_pipes(proc, &security) != 0) {
return -1;
}
proc_close_parents(proc);
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = proc->in.child;
si.hStdOutput = proc->out.child;
si.hStdError = proc->err.child;
memset(&pi, 0, sizeof(pi));
cmdline = buffer_init();