You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by di...@apache.org on 2008/07/22 06:35:46 UTC

svn commit: r678637 [38/46] - in /webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd: ./ autom4te.cache/ cygwin/ doc/ openwrt/ src/ tests/ tests/docroot/ tests/docroot/123/ tests/docroot/www/ tests/docroot/www/dummydir/ tests/docroot/www/expire/ ...

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_ssi_exprparser.h
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_ssi_exprparser.h?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_ssi_exprparser.h (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_ssi_exprparser.h Mon Jul 21 21:35:35 2008
@@ -0,0 +1,12 @@
+#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

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_ssi_exprparser.y
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_ssi_exprparser.y?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_ssi_exprparser.y (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_ssi_exprparser.y Mon Jul 21 21:35:35 2008
@@ -0,0 +1,121 @@
+%token_prefix TK_
+%token_type {buffer *}
+%extra_argument {ssi_ctx_t *ctx}
+%name ssiexprparser
+
+%include {
+#include <assert.h>
+#include <string.h>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "mod_ssi_expr.h"
+#include "buffer.h"
+}
+
+%parse_failure {
+  ctx->ok = 0;
+}
+
+%type expr { ssi_val_t * }
+%type value { buffer * }
+%type exprline { ssi_val_t * }
+%type cond { int }
+%token_destructor { buffer_free($$); }
+
+%left AND.
+%left OR.
+%nonassoc EQ NE GT GE LT LE.
+%right NOT.
+
+input ::= exprline(B). {
+  ctx->val.bo = ssi_val_tobool(B);
+  ctx->val.type = SSI_TYPE_BOOL;
+
+  ssi_val_free(B);
+}
+
+exprline(A) ::= expr(B) cond(C) expr(D). {
+  int cmp;
+
+  if (B->type == SSI_TYPE_STRING &&
+      D->type == SSI_TYPE_STRING) {
+       cmp = strcmp(B->str->ptr, D->str->ptr);
+  } else {
+    cmp = ssi_val_tobool(B) - ssi_val_tobool(D);
+  }
+
+  A = B;
+
+  switch(C) {
+  case SSI_COND_EQ: A->bo = (cmp == 0) ? 1 : 0; break;
+  case SSI_COND_NE: A->bo = (cmp != 0) ? 1 : 0; break;
+  case SSI_COND_GE: A->bo = (cmp >= 0) ? 1 : 0; break;
+  case SSI_COND_GT: A->bo = (cmp > 0) ? 1 : 0; break;
+  case SSI_COND_LE: A->bo = (cmp <= 0) ? 1 : 0; break;
+  case SSI_COND_LT: A->bo = (cmp < 0) ? 1 : 0; break;
+  }
+
+  A->type = SSI_TYPE_BOOL;
+
+  ssi_val_free(D);
+}
+exprline(A) ::= expr(B). {
+  A = B;
+}
+expr(A) ::= expr(B) AND expr(C). {
+  int e;
+
+  e = ssi_val_tobool(B) && ssi_val_tobool(C);
+
+  A = B;
+  A->bo = e;
+  A->type = SSI_TYPE_BOOL;
+  ssi_val_free(C);
+}
+
+expr(A) ::= expr(B) OR expr(C). {
+  int e;
+
+  e = ssi_val_tobool(B) || ssi_val_tobool(C);
+
+  A = B;
+  A->bo = e;
+  A->type = SSI_TYPE_BOOL;
+  ssi_val_free(C);
+}
+
+expr(A) ::= NOT expr(B). {
+  int e;
+
+  e = !ssi_val_tobool(B);
+
+  A = B;
+  A->bo = e;
+  A->type = SSI_TYPE_BOOL;
+}
+expr(A) ::= LPARAN exprline(B) RPARAN. {
+  A = B;
+}
+
+expr(A) ::= value(B). {
+  A = ssi_val_init();
+  A->str = B;
+  A->type = SSI_TYPE_STRING;
+}
+
+value(A) ::= VALUE(B). {
+  A = buffer_init_string(B->ptr);
+}
+
+value(A) ::= value(B) VALUE(C). {
+  A = B;
+  buffer_append_string_buffer(A, C);
+}
+
+cond(A) ::= EQ. { A = SSI_COND_EQ; }
+cond(A) ::= NE. { A = SSI_COND_NE; }
+cond(A) ::= LE. { A = SSI_COND_LE; }
+cond(A) ::= GE. { A = SSI_COND_GE; }
+cond(A) ::= LT. { A = SSI_COND_LT; }
+cond(A) ::= GT. { A = SSI_COND_GT; }

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_staticfile.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_staticfile.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_staticfile.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_staticfile.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,547 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "base.h"
+#include "log.h"
+#include "buffer.h"
+
+#include "plugin.h"
+
+#include "stat_cache.h"
+#include "etag.h"
+#include "http_chunk.h"
+#include "response.h"
+
+/**
+ * this is a staticfile for a lighttpd plugin
+ *
+ */
+
+
+
+/* plugin config for all request/connections */
+
+typedef struct {
+	array *exclude_ext;
+	unsigned short etags_used;
+} plugin_config;
+
+typedef struct {
+	PLUGIN_DATA;
+
+	buffer *range_buf;
+
+	plugin_config **config_storage;
+
+	plugin_config conf;
+} plugin_data;
+
+/* init the plugin data */
+INIT_FUNC(mod_staticfile_init) {
+	plugin_data *p;
+
+	p = calloc(1, sizeof(*p));
+
+	p->range_buf = buffer_init();
+
+	return p;
+}
+
+/* detroy the plugin data */
+FREE_FUNC(mod_staticfile_free) {
+	plugin_data *p = p_d;
+
+	UNUSED(srv);
+
+	if (!p) return HANDLER_GO_ON;
+
+	if (p->config_storage) {
+		size_t i;
+		for (i = 0; i < srv->config_context->used; i++) {
+			plugin_config *s = p->config_storage[i];
+
+			array_free(s->exclude_ext);
+
+			free(s);
+		}
+		free(p->config_storage);
+	}
+	buffer_free(p->range_buf);
+
+	free(p);
+
+	return HANDLER_GO_ON;
+}
+
+/* handle plugin config and check values */
+
+SETDEFAULTS_FUNC(mod_staticfile_set_defaults) {
+	plugin_data *p = p_d;
+	size_t i = 0;
+
+	config_values_t cv[] = {
+		{ "static-file.exclude-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
+		{ "static-file.etags",    NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
+		{ NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
+	};
+
+	if (!p) return HANDLER_ERROR;
+
+	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
+
+	for (i = 0; i < srv->config_context->used; i++) {
+		plugin_config *s;
+
+		s = calloc(1, sizeof(plugin_config));
+		s->exclude_ext    = array_init();
+		s->etags_used     = 1;
+
+		cv[0].destination = s->exclude_ext;
+		cv[1].destination = &(s->etags_used);
+
+		p->config_storage[i] = s;
+
+		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+			return HANDLER_ERROR;
+		}
+	}
+
+	return HANDLER_GO_ON;
+}
+
+#define PATCH(x) \
+	p->conf.x = s->x;
+static int mod_staticfile_patch_connection(server *srv, connection *con, plugin_data *p) {
+	size_t i, j;
+	plugin_config *s = p->config_storage[0];
+
+	PATCH(exclude_ext);
+	PATCH(etags_used);
+
+	/* skip the first, the global context */
+	for (i = 1; i < srv->config_context->used; i++) {
+		data_config *dc = (data_config *)srv->config_context->data[i];
+		s = p->config_storage[i];
+
+		/* condition didn't match */
+		if (!config_check_cond(srv, con, dc)) continue;
+
+		/* merge config */
+		for (j = 0; j < dc->value->used; j++) {
+			data_unset *du = dc->value->data[j];
+
+			if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.exclude-extensions"))) {
+				PATCH(exclude_ext);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.etags"))) {
+				PATCH(etags_used);
+			} 
+		}
+	}
+
+	return 0;
+}
+#undef PATCH
+
+static int http_response_parse_range(server *srv, connection *con, plugin_data *p) {
+	int multipart = 0;
+	int error;
+	off_t start, end;
+	const char *s, *minus;
+	char *boundary = "fkj49sn38dcn3";
+	data_string *ds;
+	stat_cache_entry *sce = NULL;
+	buffer *content_type = NULL;
+
+	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
+		SEGFAULT();
+	}
+
+	start = 0;
+	end = sce->st.st_size - 1;
+
+	con->response.content_length = 0;
+
+	if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) {
+		content_type = ds->value;
+	}
+
+	for (s = con->request.http_range, error = 0;
+	     !error && *s && NULL != (minus = strchr(s, '-')); ) {
+		char *err;
+		off_t la, le;
+
+		if (s == minus) {
+			/* -<stop> */
+
+			le = strtoll(s, &err, 10);
+
+			if (le == 0) {
+				/* RFC 2616 - 14.35.1 */
+
+				con->http_status = 416;
+				error = 1;
+			} else if (*err == '\0') {
+				/* end */
+				s = err;
+
+				end = sce->st.st_size - 1;
+				start = sce->st.st_size + le;
+			} else if (*err == ',') {
+				multipart = 1;
+				s = err + 1;
+
+				end = sce->st.st_size - 1;
+				start = sce->st.st_size + le;
+			} else {
+				error = 1;
+			}
+
+		} else if (*(minus+1) == '\0' || *(minus+1) == ',') {
+			/* <start>- */
+
+			la = strtoll(s, &err, 10);
+
+			if (err == minus) {
+				/* ok */
+
+				if (*(err + 1) == '\0') {
+					s = err + 1;
+
+					end = sce->st.st_size - 1;
+					start = la;
+
+				} else if (*(err + 1) == ',') {
+					multipart = 1;
+					s = err + 2;
+
+					end = sce->st.st_size - 1;
+					start = la;
+				} else {
+					error = 1;
+				}
+			} else {
+				/* error */
+				error = 1;
+			}
+		} else {
+			/* <start>-<stop> */
+
+			la = strtoll(s, &err, 10);
+
+			if (err == minus) {
+				le = strtoll(minus+1, &err, 10);
+
+				/* RFC 2616 - 14.35.1 */
+				if (la > le) {
+					error = 1;
+				}
+
+				if (*err == '\0') {
+					/* ok, end*/
+					s = err;
+
+					end = le;
+					start = la;
+				} else if (*err == ',') {
+					multipart = 1;
+					s = err + 1;
+
+					end = le;
+					start = la;
+				} else {
+					/* error */
+
+					error = 1;
+				}
+			} else {
+				/* error */
+
+				error = 1;
+			}
+		}
+
+		if (!error) {
+			if (start < 0) start = 0;
+
+			/* RFC 2616 - 14.35.1 */
+			if (end > sce->st.st_size - 1) end = sce->st.st_size - 1;
+
+			if (start > sce->st.st_size - 1) {
+				error = 1;
+
+				con->http_status = 416;
+			}
+		}
+
+		if (!error) {
+			if (multipart) {
+				/* write boundary-header */
+				buffer *b;
+
+				b = chunkqueue_get_append_buffer(con->write_queue);
+
+				buffer_copy_string(b, "\r\n--");
+				buffer_append_string(b, boundary);
+
+				/* write Content-Range */
+				buffer_append_string(b, "\r\nContent-Range: bytes ");
+				buffer_append_off_t(b, start);
+				buffer_append_string(b, "-");
+				buffer_append_off_t(b, end);
+				buffer_append_string(b, "/");
+				buffer_append_off_t(b, sce->st.st_size);
+
+				buffer_append_string(b, "\r\nContent-Type: ");
+				buffer_append_string_buffer(b, content_type);
+
+				/* write END-OF-HEADER */
+				buffer_append_string(b, "\r\n\r\n");
+
+				con->response.content_length += b->used - 1;
+
+			}
+
+			chunkqueue_append_file(con->write_queue, con->physical.path, start, end - start + 1);
+			con->response.content_length += end - start + 1;
+		}
+	}
+
+	/* something went wrong */
+	if (error) return -1;
+
+	if (multipart) {
+		/* add boundary end */
+		buffer *b;
+
+		b = chunkqueue_get_append_buffer(con->write_queue);
+
+		buffer_copy_string_len(b, "\r\n--", 4);
+		buffer_append_string(b, boundary);
+		buffer_append_string_len(b, "--\r\n", 4);
+
+		con->response.content_length += b->used - 1;
+
+		/* set header-fields */
+
+		buffer_copy_string(p->range_buf, "multipart/byteranges; boundary=");
+		buffer_append_string(p->range_buf, boundary);
+
+		/* overwrite content-type */
+		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf));
+	} else {
+		/* add Content-Range-header */
+
+		buffer_copy_string(p->range_buf, "bytes ");
+		buffer_append_off_t(p->range_buf, start);
+		buffer_append_string(p->range_buf, "-");
+		buffer_append_off_t(p->range_buf, end);
+		buffer_append_string(p->range_buf, "/");
+		buffer_append_off_t(p->range_buf, sce->st.st_size);
+
+		response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf));
+	}
+
+	/* ok, the file is set-up */
+	return 0;
+}
+
+URIHANDLER_FUNC(mod_staticfile_subrequest) {
+	plugin_data *p = p_d;
+	size_t k;
+	int s_len;
+	stat_cache_entry *sce = NULL;
+	buffer *mtime = NULL;
+	data_string *ds;
+	int allow_caching = 1;
+
+	/* someone else has done a decision for us */
+	if (con->http_status != 0) return HANDLER_GO_ON;
+	if (con->uri.path->used == 0) return HANDLER_GO_ON;
+	if (con->physical.path->used == 0) return HANDLER_GO_ON;
+
+	/* someone else has handled this request */
+	if (con->mode != DIRECT) return HANDLER_GO_ON;
+
+	/* we only handle GET, POST and HEAD */
+	switch(con->request.http_method) {
+	case HTTP_METHOD_GET:
+	case HTTP_METHOD_POST:
+	case HTTP_METHOD_HEAD:
+		break;
+	default:
+		return HANDLER_GO_ON;
+	}
+
+	mod_staticfile_patch_connection(srv, con, p);
+
+	s_len = con->uri.path->used - 1;
+
+	/* ignore certain extensions */
+	for (k = 0; k < p->conf.exclude_ext->used; k++) {
+		ds = (data_string *)p->conf.exclude_ext->data[k];
+
+		if (ds->value->used == 0) continue;
+
+		if (buffer_is_equal_right_len(con->physical.path, ds->value, ds->value->used - 1)) {
+			return HANDLER_GO_ON;
+		}
+	}
+
+
+	if (con->conf.log_request_handling) {
+		log_error_write(srv, __FILE__, __LINE__,  "s",  "-- handling file as static file");
+	}
+
+	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
+		con->http_status = 403;
+
+		log_error_write(srv, __FILE__, __LINE__, "sbsb",
+				"not a regular file:", con->uri.path,
+				"->", con->physical.path);
+
+		return HANDLER_FINISHED;
+	}
+
+	/* we only handline regular files */
+#ifdef HAVE_LSTAT
+	if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
+		con->http_status = 403;
+
+		if (con->conf.log_request_handling) {
+			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- access denied due symlink restriction");
+			log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", con->physical.path);
+		}
+
+		buffer_reset(con->physical.path);
+		return HANDLER_FINISHED;
+	}
+#endif
+	if (!S_ISREG(sce->st.st_mode)) {
+		con->http_status = 404;
+
+		if (con->conf.log_file_not_found) {
+			log_error_write(srv, __FILE__, __LINE__, "sbsb",
+					"not a regular file:", con->uri.path,
+					"->", sce->name);
+		}
+
+		return HANDLER_FINISHED;
+	}
+
+	/* mod_compress might set several data directly, don't overwrite them */
+
+	/* set response content-type, if not set already */
+
+	if (NULL == array_get_element(con->response.headers, "Content-Type")) {
+		if (buffer_is_empty(sce->content_type)) {
+			/* we are setting application/octet-stream, but also announce that
+			 * this header field might change in the seconds few requests 
+			 *
+			 * This should fix the aggressive caching of FF and the script download
+			 * seen by the first installations
+			 */
+			response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));
+
+			allow_caching = 0;
+		} else {
+			response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
+		}
+	}
+
+	if (con->conf.range_requests) {
+		response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
+	}
+
+	if (allow_caching) {
+		if (p->conf.etags_used && con->etag_flags != 0 && !buffer_is_empty(sce->etag)) {
+			if (NULL == array_get_element(con->response.headers, "ETag")) {
+				/* generate e-tag */
+				etag_mutate(con->physical.etag, sce->etag);
+
+				response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
+			}
+		}
+
+		/* prepare header */
+		if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) {
+			mtime = strftime_cache_get(srv, sce->st.st_mtime);
+			response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
+		} else {
+			mtime = ds->value;
+		}
+
+		if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
+			return HANDLER_FINISHED;
+		}
+	}
+
+	if (con->request.http_range && con->conf.range_requests) {
+		int do_range_request = 1;
+		/* check if we have a conditional GET */
+
+		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
+			/* if the value is the same as our ETag, we do a Range-request,
+			 * otherwise a full 200 */
+
+			if (ds->value->ptr[0] == '"') {
+				/**
+				 * client wants a ETag
+				 */
+				if (!con->physical.etag) {
+					do_range_request = 0;
+				} else if (!buffer_is_equal(ds->value, con->physical.etag)) {
+					do_range_request = 0;
+				}
+			} else if (!mtime) {
+				/**
+				 * we don't have a Last-Modified and can match the If-Range: 
+				 *
+				 * sending all
+				 */
+				do_range_request = 0;
+			} else if (!buffer_is_equal(ds->value, mtime)) {
+				do_range_request = 0;
+			}
+		}
+
+		if (do_range_request) {
+			/* content prepared, I'm done */
+			con->file_finished = 1;
+
+			if (0 == http_response_parse_range(srv, con, p)) {
+				con->http_status = 206;
+			}
+			return HANDLER_FINISHED;
+		}
+	}
+
+	/* if we are still here, prepare body */
+
+	/* we add it here for all requests
+	 * the HEAD request will drop it afterwards again
+	 */
+	http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size);
+
+	con->http_status = 200;
+	con->file_finished = 1;
+
+	return HANDLER_FINISHED;
+}
+
+/* this function is called at dlopen() time and inits the callbacks */
+
+int mod_staticfile_plugin_init(plugin *p) {
+	p->version     = LIGHTTPD_VERSION_ID;
+	p->name        = buffer_init_string("staticfile");
+
+	p->init        = mod_staticfile_init;
+	p->handle_subrequest_start = mod_staticfile_subrequest;
+	p->set_defaults  = mod_staticfile_set_defaults;
+	p->cleanup     = mod_staticfile_free;
+
+	p->data        = NULL;
+
+	return 0;
+}

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_status.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_status.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_status.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_status.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,873 @@
+#define _GNU_SOURCE
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <stdio.h>
+
+#include "server.h"
+#include "connections.h"
+#include "response.h"
+#include "connections.h"
+#include "log.h"
+
+#include "plugin.h"
+
+#include "inet_ntop_cache.h"
+
+typedef struct {
+	buffer *config_url;
+	buffer *status_url;
+	buffer *statistics_url;
+
+	int     sort;
+} plugin_config;
+
+typedef struct {
+	PLUGIN_DATA;
+
+	double traffic_out;
+	double requests;
+
+	double mod_5s_traffic_out[5];
+	double mod_5s_requests[5];
+	size_t mod_5s_ndx;
+
+	double rel_traffic_out;
+	double rel_requests;
+
+	double abs_traffic_out;
+	double abs_requests;
+
+	double bytes_written;
+
+	buffer *module_list;
+
+	plugin_config **config_storage;
+
+	plugin_config conf;
+} plugin_data;
+
+INIT_FUNC(mod_status_init) {
+	plugin_data *p;
+	size_t i;
+
+	p = calloc(1, sizeof(*p));
+
+	p->traffic_out = p->requests = 0;
+	p->rel_traffic_out = p->rel_requests = 0;
+	p->abs_traffic_out = p->abs_requests = 0;
+	p->bytes_written = 0;
+	p->module_list = buffer_init();
+
+	for (i = 0; i < 5; i++) {
+		p->mod_5s_traffic_out[i] = p->mod_5s_requests[i] = 0;
+	}
+
+	return p;
+}
+
+FREE_FUNC(mod_status_free) {
+	plugin_data *p = p_d;
+
+	UNUSED(srv);
+
+	if (!p) return HANDLER_GO_ON;
+
+	buffer_free(p->module_list);
+
+	if (p->config_storage) {
+		size_t i;
+		for (i = 0; i < srv->config_context->used; i++) {
+			plugin_config *s = p->config_storage[i];
+
+			buffer_free(s->status_url);
+			buffer_free(s->statistics_url);
+			buffer_free(s->config_url);
+
+			free(s);
+		}
+		free(p->config_storage);
+	}
+
+
+	free(p);
+
+	return HANDLER_GO_ON;
+}
+
+SETDEFAULTS_FUNC(mod_status_set_defaults) {
+	plugin_data *p = p_d;
+	size_t i;
+
+	config_values_t cv[] = {
+		{ "status.status-url",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
+		{ "status.config-url",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
+		{ "status.enable-sort",          NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
+		{ "status.statistics-url",       NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
+		{ NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
+	};
+
+	if (!p) return HANDLER_ERROR;
+
+	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
+
+	for (i = 0; i < srv->config_context->used; i++) {
+		plugin_config *s;
+
+		s = calloc(1, sizeof(plugin_config));
+		s->config_url    = buffer_init();
+		s->status_url    = buffer_init();
+		s->sort          = 1;
+		s->statistics_url    = buffer_init();
+
+		cv[0].destination = s->status_url;
+		cv[1].destination = s->config_url;
+		cv[2].destination = &(s->sort);
+		cv[3].destination = s->statistics_url;
+
+		p->config_storage[i] = s;
+
+		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+			return HANDLER_ERROR;
+		}
+	}
+
+	return HANDLER_GO_ON;
+}
+
+
+
+static int mod_status_row_append(buffer *b, const char *key, const char *value) {
+	BUFFER_APPEND_STRING_CONST(b, "   <tr>\n");
+	BUFFER_APPEND_STRING_CONST(b, "    <td><b>");
+	buffer_append_string(b, key);
+	BUFFER_APPEND_STRING_CONST(b, "</b></td>\n");
+	BUFFER_APPEND_STRING_CONST(b, "    <td>");
+	buffer_append_string(b, value);
+	BUFFER_APPEND_STRING_CONST(b, "</td>\n");
+	BUFFER_APPEND_STRING_CONST(b, "   </tr>\n");
+
+	return 0;
+}
+
+static int mod_status_header_append(buffer *b, const char *key) {
+	BUFFER_APPEND_STRING_CONST(b, "   <tr>\n");
+	BUFFER_APPEND_STRING_CONST(b, "    <th colspan=\"2\">");
+	buffer_append_string(b, key);
+	BUFFER_APPEND_STRING_CONST(b, "</th>\n");
+	BUFFER_APPEND_STRING_CONST(b, "   </tr>\n");
+
+	return 0;
+}
+
+static int mod_status_header_append_sort(buffer *b, void *p_d, const char* key) {
+	plugin_data *p = p_d;
+
+	if (p->conf.sort) {
+		BUFFER_APPEND_STRING_CONST(b, "<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">");
+		buffer_append_string(b, key);
+		BUFFER_APPEND_STRING_CONST(b, "<span class=\"sortarrow\">:</span></a></th>\n");
+	} else {
+		BUFFER_APPEND_STRING_CONST(b, "<th class=\"status\">");
+		buffer_append_string(b, key);
+		BUFFER_APPEND_STRING_CONST(b, "</th>\n");
+	}
+
+	return 0;
+}
+
+static int mod_status_get_multiplier(double *avg, char *multiplier, int size) {
+	*multiplier = ' ';
+
+	if (*avg > size) { *avg /= size; *multiplier = 'k'; }
+	if (*avg > size) { *avg /= size; *multiplier = 'M'; }
+	if (*avg > size) { *avg /= size; *multiplier = 'G'; }
+	if (*avg > size) { *avg /= size; *multiplier = 'T'; }
+	if (*avg > size) { *avg /= size; *multiplier = 'P'; }
+	if (*avg > size) { *avg /= size; *multiplier = 'E'; }
+	if (*avg > size) { *avg /= size; *multiplier = 'Z'; }
+	if (*avg > size) { *avg /= size; *multiplier = 'Y'; }
+
+	return 0;
+}
+
+static handler_t mod_status_handle_server_status_html(server *srv, connection *con, void *p_d) {
+	plugin_data *p = p_d;
+	buffer *b;
+	size_t j;
+	double avg;
+	char multiplier = '\0';
+	char buf[32];
+	time_t ts;
+
+	int days, hours, mins, seconds;
+
+	b = chunkqueue_get_append_buffer(con->write_queue);
+
+	BUFFER_COPY_STRING_CONST(b,
+				 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
+				 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
+				 "         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
+				 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
+				 " <head>\n"
+				 "  <title>Status</title>\n");
+
+	BUFFER_APPEND_STRING_CONST(b,
+				   "  <style type=\"text/css\">\n"
+				   "    table.status { border: black solid thin; }\n"
+				   "    td { white-space: nowrap; }\n"
+				   "    td.int { background-color: #f0f0f0; text-align: right }\n"
+				   "    td.string { background-color: #f0f0f0; text-align: left }\n"
+				   "    th.status { background-color: black; color: white; font-weight: bold; }\n"
+				   "    a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n"
+				   "    span.sortarrow { color: white; text-decoration: none; }\n"
+				   "  </style>\n");
+
+	if (p->conf.sort) {
+		BUFFER_APPEND_STRING_CONST(b,
+					   "<script type=\"text/javascript\">\n"
+					   "// <!--\n"
+					   "var sort_column;\n"
+					   "var prev_span = null;\n");
+
+		BUFFER_APPEND_STRING_CONST(b,
+					   "function get_inner_text(el) {\n"
+					   " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
+					   "  return el;\n"
+					   " if(el.innerText)\n"
+					   "  return el.innerText;\n"
+					   " else {\n"
+					   "  var str = \"\";\n"
+					   "  var cs = el.childNodes;\n"
+					   "  var l = cs.length;\n"
+					   "  for (i=0;i<l;i++) {\n"
+					   "   if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n"
+					   "   else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n"
+					   "  }\n"
+					   " }\n"
+					   " return str;\n"
+					   "}\n");
+
+		BUFFER_APPEND_STRING_CONST(b,
+					   "function sortfn(a,b) {\n"
+					   " var at = get_inner_text(a.cells[sort_column]);\n"
+					   " var bt = get_inner_text(b.cells[sort_column]);\n"
+					   " if (a.cells[sort_column].className == 'int') {\n"
+					   "  return parseInt(at)-parseInt(bt);\n"
+					   " } else {\n"
+					   "  aa = at.toLowerCase();\n"
+					   "  bb = bt.toLowerCase();\n"
+					   "  if (aa==bb) return 0;\n"
+					   "  else if (aa<bb) return -1;\n"
+					   "  else return 1;\n"
+					   " }\n"
+					   "}\n");
+
+		BUFFER_APPEND_STRING_CONST(b,
+					   "function resort(lnk) {\n"
+					   " var span = lnk.childNodes[1];\n"
+					   " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n"
+					   " var rows = new Array();\n"
+					   " for (j=1;j<table.rows.length;j++)\n"
+					   "  rows[j-1] = table.rows[j];\n"
+					   " sort_column = lnk.parentNode.cellIndex;\n"
+					   " rows.sort(sortfn);\n");
+
+		BUFFER_APPEND_STRING_CONST(b,
+					   " if (prev_span != null) prev_span.innerHTML = '';\n"
+					   " if (span.getAttribute('sortdir')=='down') {\n"
+					   "  span.innerHTML = '&uarr;';\n"
+					   "  span.setAttribute('sortdir','up');\n"
+					   "  rows.reverse();\n"
+					   " } else {\n"
+					   "  span.innerHTML = '&darr;';\n"
+					   "  span.setAttribute('sortdir','down');\n"
+					   " }\n"
+					   " for (i=0;i<rows.length;i++)\n"
+					   "  table.tBodies[0].appendChild(rows[i]);\n"
+					   " prev_span = span;\n"
+					   "}\n"
+					   "// -->\n"
+					   "</script>\n");
+	}
+
+	BUFFER_APPEND_STRING_CONST(b,
+				 " </head>\n"
+				 " <body>\n");
+
+
+
+	/* connection listing */
+	BUFFER_APPEND_STRING_CONST(b, "<h1>Server-Status</h1>");
+
+	BUFFER_APPEND_STRING_CONST(b, "<table summary=\"status\" class=\"status\">");
+	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Hostname</td><td class=\"string\">");
+	buffer_append_string_buffer(b, con->uri.authority);
+	BUFFER_APPEND_STRING_CONST(b, " (");
+	buffer_append_string_buffer(b, con->server_name);
+	BUFFER_APPEND_STRING_CONST(b, ")</td></tr>\n");
+	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Uptime</td><td class=\"string\">");
+
+	ts = srv->cur_ts - srv->startup_ts;
+
+	days = ts / (60 * 60 * 24);
+	ts %= (60 * 60 * 24);
+
+	hours = ts / (60 * 60);
+	ts %= (60 * 60);
+
+	mins = ts / (60);
+	ts %= (60);
+
+	seconds = ts;
+
+	if (days) {
+		buffer_append_long(b, days);
+		BUFFER_APPEND_STRING_CONST(b, " days ");
+	}
+
+	if (hours) {
+		buffer_append_long(b, hours);
+		BUFFER_APPEND_STRING_CONST(b, " hours ");
+	}
+
+	if (mins) {
+		buffer_append_long(b, mins);
+		BUFFER_APPEND_STRING_CONST(b, " min ");
+	}
+
+	buffer_append_long(b, seconds);
+	BUFFER_APPEND_STRING_CONST(b, " s");
+
+	BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n");
+	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Started at</td><td class=\"string\">");
+
+	ts = srv->startup_ts;
+
+	strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts));
+	buffer_append_string(b, buf);
+	BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n");
+
+
+	BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">absolute (since start)</th></tr>\n");
+
+	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">");
+	avg = p->abs_requests;
+
+	mod_status_get_multiplier(&avg, &multiplier, 1000);
+
+	buffer_append_long(b, avg);
+	BUFFER_APPEND_STRING_CONST(b, " ");
+	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
+	BUFFER_APPEND_STRING_CONST(b, "req</td></tr>\n");
+
+	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Traffic</td><td class=\"string\">");
+	avg = p->abs_traffic_out;
+
+	mod_status_get_multiplier(&avg, &multiplier, 1024);
+
+	sprintf(buf, "%.2f", avg);
+	buffer_append_string(b, buf);
+	BUFFER_APPEND_STRING_CONST(b, " ");
+	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
+	BUFFER_APPEND_STRING_CONST(b, "byte</td></tr>\n");
+
+
+
+	BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">average (since start)</th></tr>\n");
+
+	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">");
+	avg = p->abs_requests / (srv->cur_ts - srv->startup_ts);
+
+	mod_status_get_multiplier(&avg, &multiplier, 1000);
+
+	buffer_append_long(b, avg);
+	BUFFER_APPEND_STRING_CONST(b, " ");
+	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
+	BUFFER_APPEND_STRING_CONST(b, "req/s</td></tr>\n");
+
+	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Traffic</td><td class=\"string\">");
+	avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts);
+
+	mod_status_get_multiplier(&avg, &multiplier, 1024);
+
+	sprintf(buf, "%.2f", avg);
+	buffer_append_string(b, buf);
+	BUFFER_APPEND_STRING_CONST(b, " ");
+	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
+	BUFFER_APPEND_STRING_CONST(b, "byte/s</td></tr>\n");
+
+
+
+	BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n");
+	for (j = 0, avg = 0; j < 5; j++) {
+		avg += p->mod_5s_requests[j];
+	}
+
+	avg /= 5;
+
+	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">");
+
+	mod_status_get_multiplier(&avg, &multiplier, 1000);
+
+	buffer_append_long(b, avg);
+	BUFFER_APPEND_STRING_CONST(b, " ");
+	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
+
+	BUFFER_APPEND_STRING_CONST(b, "req/s</td></tr>\n");
+
+	for (j = 0, avg = 0; j < 5; j++) {
+		avg += p->mod_5s_traffic_out[j];
+	}
+
+	avg /= 5;
+
+	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Traffic</td><td class=\"string\">");
+
+	mod_status_get_multiplier(&avg, &multiplier, 1024);
+
+	sprintf(buf, "%.2f", avg);
+	buffer_append_string(b, buf);
+	BUFFER_APPEND_STRING_CONST(b, " ");
+	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
+	BUFFER_APPEND_STRING_CONST(b, "byte/s</td></tr>\n");
+
+	BUFFER_APPEND_STRING_CONST(b, "</table>\n");
+
+
+	BUFFER_APPEND_STRING_CONST(b, "<hr />\n<pre><b>legend</b>\n");
+	BUFFER_APPEND_STRING_CONST(b, ". = connect, C = close, E = hard error\n");
+	BUFFER_APPEND_STRING_CONST(b, "r = read, R = read-POST, W = write, h = handle-request\n");
+	BUFFER_APPEND_STRING_CONST(b, "q = request-start,  Q = request-end\n");
+	BUFFER_APPEND_STRING_CONST(b, "s = response-start, S = response-end\n");
+
+	BUFFER_APPEND_STRING_CONST(b, "<b>");
+	buffer_append_long(b, srv->conns->used);
+	BUFFER_APPEND_STRING_CONST(b, " connections</b>\n");
+
+	for (j = 0; j < srv->conns->used; j++) {
+		connection *c = srv->conns->ptr[j];
+		const char *state = connection_get_short_state(c->state);
+
+		buffer_append_string_len(b, state, 1);
+
+		if (((j + 1) % 50) == 0) {
+			BUFFER_APPEND_STRING_CONST(b, "\n");
+		}
+	}
+
+	BUFFER_APPEND_STRING_CONST(b, "\n</pre><hr />\n<h2>Connections</h2>\n");
+
+	BUFFER_APPEND_STRING_CONST(b, "<table summary=\"status\" class=\"status\">\n");
+	BUFFER_APPEND_STRING_CONST(b, "<tr>");
+	mod_status_header_append_sort(b, p_d, "Client IP");
+	mod_status_header_append_sort(b, p_d, "Read");
+	mod_status_header_append_sort(b, p_d, "Written");
+	mod_status_header_append_sort(b, p_d, "State");
+	mod_status_header_append_sort(b, p_d, "Time");
+	mod_status_header_append_sort(b, p_d, "Host");
+	mod_status_header_append_sort(b, p_d, "URI");
+	mod_status_header_append_sort(b, p_d, "File");
+	BUFFER_APPEND_STRING_CONST(b, "</tr>\n");
+
+	for (j = 0; j < srv->conns->used; j++) {
+		connection *c = srv->conns->ptr[j];
+
+		BUFFER_APPEND_STRING_CONST(b, "<tr><td class=\"string\">");
+
+		buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr)));
+
+		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">");
+
+		if (con->request.content_length) {
+			buffer_append_long(b, c->request_content_queue->bytes_in);
+			BUFFER_APPEND_STRING_CONST(b, "/");
+			buffer_append_long(b, c->request.content_length);
+		} else {
+			BUFFER_APPEND_STRING_CONST(b, "0/0");
+		}
+
+		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">");
+
+		buffer_append_off_t(b, chunkqueue_written(c->write_queue));
+		BUFFER_APPEND_STRING_CONST(b, "/");
+		buffer_append_off_t(b, chunkqueue_length(c->write_queue));
+
+		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">");
+
+		buffer_append_string(b, connection_get_state(c->state));
+
+		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">");
+
+		buffer_append_long(b, srv->cur_ts - c->request_start);
+
+		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">");
+
+		if (buffer_is_empty(c->server_name)) {
+			buffer_append_string_buffer(b, c->uri.authority);
+		}
+		else {
+			buffer_append_string_buffer(b, c->server_name);
+		}
+
+		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">");
+
+		if (!buffer_is_empty(c->uri.path)) {
+			buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML);
+		}
+
+		if (!buffer_is_empty(c->uri.query)) {
+			BUFFER_APPEND_STRING_CONST(b, "?");
+			buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.query), ENCODING_HTML);
+		}
+
+		if (!buffer_is_empty(c->request.orig_uri)) {
+			BUFFER_APPEND_STRING_CONST(b, " (");
+			buffer_append_string_encoded(b, CONST_BUF_LEN(c->request.orig_uri), ENCODING_HTML);
+			BUFFER_APPEND_STRING_CONST(b, ")");
+		}
+		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">");
+
+		buffer_append_string_buffer(b, c->physical.path);
+
+		BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n");
+	}
+
+
+	BUFFER_APPEND_STRING_CONST(b,
+		      "</table>\n");
+
+
+	BUFFER_APPEND_STRING_CONST(b,
+		      " </body>\n"
+		      "</html>\n"
+		      );
+
+	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
+
+	return 0;
+}
+
+
+static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) {
+	plugin_data *p = p_d;
+	buffer *b;
+	double avg;
+	time_t ts;
+	char buf[32];
+	unsigned int k;
+	unsigned int l;
+
+	b = chunkqueue_get_append_buffer(con->write_queue);
+
+	/* output total number of requests */
+	BUFFER_APPEND_STRING_CONST(b, "Total Accesses: ");
+	avg = p->abs_requests;
+	snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
+	buffer_append_string(b, buf);
+	BUFFER_APPEND_STRING_CONST(b, "\n");
+
+	/* output total traffic out in kbytes */
+	BUFFER_APPEND_STRING_CONST(b, "Total kBytes: ");
+	avg = p->abs_traffic_out / 1024;
+	snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
+	buffer_append_string(b, buf);
+	BUFFER_APPEND_STRING_CONST(b, "\n");
+
+	/* output uptime */
+	BUFFER_APPEND_STRING_CONST(b, "Uptime: ");
+	ts = srv->cur_ts - srv->startup_ts;
+	buffer_append_long(b, ts);
+	BUFFER_APPEND_STRING_CONST(b, "\n");
+
+	/* output busy servers */
+	BUFFER_APPEND_STRING_CONST(b, "BusyServers: ");
+	buffer_append_long(b, srv->conns->used);
+	BUFFER_APPEND_STRING_CONST(b, "\n");
+
+	BUFFER_APPEND_STRING_CONST(b, "IdleServers: ");
+       buffer_append_long(b, srv->conns->size - srv->conns->used);
+       BUFFER_APPEND_STRING_CONST(b, "\n");
+
+       /* output scoreboard */
+       BUFFER_APPEND_STRING_CONST(b, "Scoreboard: ");
+       for (k = 0; k < srv->conns->used; k++) {
+        	connection *c = srv->conns->ptr[k];
+		const char *state = connection_get_short_state(c->state);
+		buffer_append_string_len(b, state, 1);
+	}
+	for (l = 0; l < srv->conns->size - srv->conns->used; l++) {
+		BUFFER_APPEND_STRING_CONST(b, "_");
+	}
+	BUFFER_APPEND_STRING_CONST(b, "\n");
+
+	/* set text/plain output */
+
+	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
+
+	return 0;
+}
+
+static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) {
+	plugin_data *p = p_d;
+	buffer *b = p->module_list;
+	size_t i;
+	array *st = srv->status;
+
+	if (0 == st->used) {
+		/* we have nothing to send */
+		con->http_status = 204;
+		con->file_finished = 1;
+
+		return HANDLER_FINISHED;
+	}
+
+	b = chunkqueue_get_append_buffer(con->write_queue);
+
+	for (i = 0; i < st->used; i++) {
+		size_t ndx = st->sorted[i];
+
+		buffer_append_string_buffer(b, st->data[ndx]->key);
+		buffer_append_string(b, ": ");
+		buffer_append_long(b, ((data_integer *)(st->data[ndx]))->value);
+		buffer_append_string(b, "\n");
+	}
+
+	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
+
+	con->http_status = 200;
+	con->file_finished = 1;
+
+	return HANDLER_FINISHED;
+}
+
+
+static handler_t mod_status_handle_server_status(server *srv, connection *con, void *p_d) {
+
+	if (buffer_is_equal_string(con->uri.query, CONST_STR_LEN("auto"))) {
+		mod_status_handle_server_status_text(srv, con, p_d);
+	} else {
+		mod_status_handle_server_status_html(srv, con, p_d);
+	}
+
+	con->http_status = 200;
+	con->file_finished = 1;
+
+	return HANDLER_FINISHED;
+}
+
+
+static handler_t mod_status_handle_server_config(server *srv, connection *con, void *p_d) {
+	plugin_data *p = p_d;
+	buffer *b, *m = p->module_list;
+	size_t i;
+
+	struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] =
+	{
+		/* - poll is most reliable
+		 * - select works everywhere
+		 * - linux-* are experimental
+		 */
+#ifdef USE_POLL
+		{ FDEVENT_HANDLER_POLL,           "poll" },
+#endif
+#ifdef USE_SELECT
+		{ FDEVENT_HANDLER_SELECT,         "select" },
+#endif
+#ifdef USE_LINUX_EPOLL
+		{ FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" },
+#endif
+#ifdef USE_LINUX_SIGIO
+		{ FDEVENT_HANDLER_LINUX_RTSIG,    "linux-rtsig" },
+#endif
+#ifdef USE_SOLARIS_DEVPOLL
+		{ FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
+#endif
+#ifdef USE_FREEBSD_KQUEUE
+		{ FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
+#endif
+		{ FDEVENT_HANDLER_UNSET,          NULL }
+	};
+
+	b = chunkqueue_get_append_buffer(con->write_queue);
+
+	BUFFER_COPY_STRING_CONST(b,
+			   "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
+			   "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
+			   "         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
+			   "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
+			   " <head>\n"
+			   "  <title>Status</title>\n"
+			   " </head>\n"
+			   " <body>\n"
+			   "  <h1>" PACKAGE_NAME " " PACKAGE_VERSION "</h1>\n"
+			   "  <table summary=\"status\" border=\"1\">\n");
+
+	mod_status_header_append(b, "Server-Features");
+#ifdef HAVE_PCRE_H
+	mod_status_row_append(b, "RegEx Conditionals", "enabled");
+#else
+	mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing");
+#endif
+	mod_status_header_append(b, "Network Engine");
+
+	for (i = 0; event_handlers[i].name; i++) {
+		if (event_handlers[i].et == srv->event_handler) {
+			mod_status_row_append(b, "fd-Event-Handler", event_handlers[i].name);
+			break;
+		}
+	}
+
+	mod_status_header_append(b, "Config-File-Settings");
+
+	for (i = 0; i < srv->plugins.used; i++) {
+		plugin **ps = srv->plugins.ptr;
+
+		plugin *pl = ps[i];
+
+		if (i == 0) {
+			buffer_copy_string_buffer(m, pl->name);
+		} else {
+			BUFFER_APPEND_STRING_CONST(m, "<br />");
+			buffer_append_string_buffer(m, pl->name);
+		}
+	}
+
+	mod_status_row_append(b, "Loaded Modules", m->ptr);
+
+	BUFFER_APPEND_STRING_CONST(b, "  </table>\n");
+
+	BUFFER_APPEND_STRING_CONST(b,
+		      " </body>\n"
+		      "</html>\n"
+		      );
+
+	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
+
+	con->http_status = 200;
+	con->file_finished = 1;
+
+	return HANDLER_FINISHED;
+}
+
+#define PATCH(x) \
+	p->conf.x = s->x;
+static int mod_status_patch_connection(server *srv, connection *con, plugin_data *p) {
+	size_t i, j;
+	plugin_config *s = p->config_storage[0];
+
+	PATCH(status_url);
+	PATCH(config_url);
+	PATCH(sort);
+	PATCH(statistics_url);
+
+	/* skip the first, the global context */
+	for (i = 1; i < srv->config_context->used; i++) {
+		data_config *dc = (data_config *)srv->config_context->data[i];
+		s = p->config_storage[i];
+
+		/* condition didn't match */
+		if (!config_check_cond(srv, con, dc)) continue;
+
+		/* merge config */
+		for (j = 0; j < dc->value->used; j++) {
+			data_unset *du = dc->value->data[j];
+
+			if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.status-url"))) {
+				PATCH(status_url);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) {
+				PATCH(config_url);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) {
+				PATCH(sort);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) {
+				PATCH(statistics_url);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static handler_t mod_status_handler(server *srv, connection *con, void *p_d) {
+	plugin_data *p = p_d;
+
+	mod_status_patch_connection(srv, con, p);
+
+	if (!buffer_is_empty(p->conf.status_url) &&
+	    buffer_is_equal(p->conf.status_url, con->uri.path)) {
+		return mod_status_handle_server_status(srv, con, p_d);
+	} else if (!buffer_is_empty(p->conf.config_url) &&
+	    buffer_is_equal(p->conf.config_url, con->uri.path)) {
+		return mod_status_handle_server_config(srv, con, p_d);
+	} else if (!buffer_is_empty(p->conf.statistics_url) &&
+	    buffer_is_equal(p->conf.statistics_url, con->uri.path)) {
+		return mod_status_handle_server_statistics(srv, con, p_d);
+	}
+
+	return HANDLER_GO_ON;
+}
+
+TRIGGER_FUNC(mod_status_trigger) {
+	plugin_data *p = p_d;
+	size_t i;
+
+	/* check all connections */
+	for (i = 0; i < srv->conns->used; i++) {
+		connection *c = srv->conns->ptr[i];
+
+		p->bytes_written += c->bytes_written_cur_second;
+	}
+
+	/* a sliding average */
+	p->mod_5s_traffic_out[p->mod_5s_ndx] = p->bytes_written;
+	p->mod_5s_requests   [p->mod_5s_ndx] = p->requests;
+
+	p->mod_5s_ndx = (p->mod_5s_ndx+1) % 5;
+
+	p->abs_traffic_out += p->bytes_written;
+	p->rel_traffic_out += p->bytes_written;
+
+	p->bytes_written = 0;
+
+	/* reset storage - second */
+	p->traffic_out = 0;
+	p->requests    = 0;
+
+	return HANDLER_GO_ON;
+}
+
+REQUESTDONE_FUNC(mod_status_account) {
+	plugin_data *p = p_d;
+
+	UNUSED(srv);
+
+	p->requests++;
+	p->rel_requests++;
+	p->abs_requests++;
+
+	p->bytes_written += con->bytes_written_cur_second;
+
+	return HANDLER_GO_ON;
+}
+
+int mod_status_plugin_init(plugin *p) {
+	p->version     = LIGHTTPD_VERSION_ID;
+	p->name        = buffer_init_string("status");
+
+	p->init        = mod_status_init;
+	p->cleanup     = mod_status_free;
+	p->set_defaults= mod_status_set_defaults;
+
+	p->handle_uri_clean    = mod_status_handler;
+	p->handle_trigger      = mod_status_trigger;
+	p->handle_request_done = mod_status_account;
+
+	p->data        = NULL;
+
+	return 0;
+}

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_trigger_b4_dl.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_trigger_b4_dl.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_trigger_b4_dl.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_trigger_b4_dl.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,586 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base.h"
+#include "log.h"
+#include "buffer.h"
+
+#include "plugin.h"
+#include "response.h"
+#include "inet_ntop_cache.h"
+
+#if defined(HAVE_GDBM_H)
+#include <gdbm.h>
+#endif
+
+#if defined(HAVE_PCRE_H)
+#include <pcre.h>
+#endif
+
+#if defined(HAVE_MEMCACHE_H)
+#include <memcache.h>
+#endif
+
+/**
+ * this is a trigger_b4_dl for a lighttpd plugin
+ *
+ */
+
+/* plugin config for all request/connections */
+
+typedef struct {
+	buffer *db_filename;
+
+	buffer *trigger_url;
+	buffer *download_url;
+	buffer *deny_url;
+
+	array  *mc_hosts;
+	buffer *mc_namespace;
+#if defined(HAVE_PCRE_H)
+	pcre *trigger_regex;
+	pcre *download_regex;
+#endif
+#if defined(HAVE_GDBM_H)
+	GDBM_FILE db;
+#endif
+
+#if defined(HAVE_MEMCACHE_H)
+	struct memcache *mc;
+#endif
+
+	unsigned short trigger_timeout;
+	unsigned short debug;
+} plugin_config;
+
+typedef struct {
+	PLUGIN_DATA;
+
+	buffer *tmp_buf;
+
+	plugin_config **config_storage;
+
+	plugin_config conf;
+} plugin_data;
+
+/* init the plugin data */
+INIT_FUNC(mod_trigger_b4_dl_init) {
+	plugin_data *p;
+
+	p = calloc(1, sizeof(*p));
+
+	p->tmp_buf = buffer_init();
+
+	return p;
+}
+
+/* detroy the plugin data */
+FREE_FUNC(mod_trigger_b4_dl_free) {
+	plugin_data *p = p_d;
+
+	UNUSED(srv);
+
+	if (!p) return HANDLER_GO_ON;
+
+	if (p->config_storage) {
+		size_t i;
+		for (i = 0; i < srv->config_context->used; i++) {
+			plugin_config *s = p->config_storage[i];
+
+			if (!s) continue;
+
+			buffer_free(s->db_filename);
+			buffer_free(s->download_url);
+			buffer_free(s->trigger_url);
+			buffer_free(s->deny_url);
+
+			buffer_free(s->mc_namespace);
+			array_free(s->mc_hosts);
+
+#if defined(HAVE_PCRE_H)
+			if (s->trigger_regex) pcre_free(s->trigger_regex);
+			if (s->download_regex) pcre_free(s->download_regex);
+#endif
+#if defined(HAVE_GDBM_H)
+			if (s->db) gdbm_close(s->db);
+#endif
+#if defined(HAVE_MEMCACHE_H)
+			if (s->mc) mc_free(s->mc);
+#endif
+
+			free(s);
+		}
+		free(p->config_storage);
+	}
+
+	buffer_free(p->tmp_buf);
+
+	free(p);
+
+	return HANDLER_GO_ON;
+}
+
+/* handle plugin config and check values */
+
+SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) {
+	plugin_data *p = p_d;
+	size_t i = 0;
+
+
+	config_values_t cv[] = {
+		{ "trigger-before-download.gdbm-filename",   NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
+		{ "trigger-before-download.trigger-url",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
+		{ "trigger-before-download.download-url",    NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
+		{ "trigger-before-download.deny-url",        NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
+		{ "trigger-before-download.trigger-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 4 */
+		{ "trigger-before-download.memcache-hosts",  NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },        /* 5 */
+		{ "trigger-before-download.memcache-namespace", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },    /* 6 */
+		{ "trigger-before-download.debug",           NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },      /* 7 */
+		{ NULL,                        NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
+	};
+
+	if (!p) return HANDLER_ERROR;
+
+	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
+
+	for (i = 0; i < srv->config_context->used; i++) {
+		plugin_config *s;
+#if defined(HAVE_PCRE_H)
+		const char *errptr;
+		int erroff;
+#endif
+
+		s = calloc(1, sizeof(plugin_config));
+		s->db_filename    = buffer_init();
+		s->download_url   = buffer_init();
+		s->trigger_url    = buffer_init();
+		s->deny_url       = buffer_init();
+		s->mc_hosts       = array_init();
+		s->mc_namespace   = buffer_init();
+
+		cv[0].destination = s->db_filename;
+		cv[1].destination = s->trigger_url;
+		cv[2].destination = s->download_url;
+		cv[3].destination = s->deny_url;
+		cv[4].destination = &(s->trigger_timeout);
+		cv[5].destination = s->mc_hosts;
+		cv[6].destination = s->mc_namespace;
+		cv[7].destination = &(s->debug);
+
+		p->config_storage[i] = s;
+
+		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+			return HANDLER_ERROR;
+		}
+#if defined(HAVE_GDBM_H)
+		if (!buffer_is_empty(s->db_filename)) {
+			if (NULL == (s->db = gdbm_open(s->db_filename->ptr, 4096, GDBM_WRCREAT | GDBM_NOLOCK, S_IRUSR | S_IWUSR, 0))) {
+				log_error_write(srv, __FILE__, __LINE__, "s",
+						"gdbm-open failed");
+				return HANDLER_ERROR;
+			}
+		}
+#endif
+#if defined(HAVE_PCRE_H)
+		if (!buffer_is_empty(s->download_url)) {
+			if (NULL == (s->download_regex = pcre_compile(s->download_url->ptr,
+								      0, &errptr, &erroff, NULL))) {
+
+				log_error_write(srv, __FILE__, __LINE__, "sbss",
+						"compiling regex for download-url failed:",
+						s->download_url, "pos:", erroff);
+				return HANDLER_ERROR;
+			}
+		}
+
+		if (!buffer_is_empty(s->trigger_url)) {
+			if (NULL == (s->trigger_regex = pcre_compile(s->trigger_url->ptr,
+								     0, &errptr, &erroff, NULL))) {
+
+				log_error_write(srv, __FILE__, __LINE__, "sbss",
+						"compiling regex for trigger-url failed:",
+						s->trigger_url, "pos:", erroff);
+
+				return HANDLER_ERROR;
+			}
+		}
+#endif
+
+		if (s->mc_hosts->used) {
+#if defined(HAVE_MEMCACHE_H)
+			size_t k;
+			s->mc = mc_new();
+
+			for (k = 0; k < s->mc_hosts->used; k++) {
+				data_string *ds = (data_string *)s->mc_hosts->data[k];
+
+				if (0 != mc_server_add4(s->mc, ds->value->ptr)) {
+					log_error_write(srv, __FILE__, __LINE__, "sb",
+							"connection to host failed:",
+							ds->value);
+
+					return HANDLER_ERROR;
+				}
+			}
+#else
+			log_error_write(srv, __FILE__, __LINE__, "s",
+					"memcache support is not compiled in but trigger-before-download.memcache-hosts is set, aborting");
+			return HANDLER_ERROR;
+#endif
+		}
+
+
+#if (!defined(HAVE_GDBM_H) && !defined(HAVE_MEMCACHE_H)) || !defined(HAVE_PCRE_H)
+		log_error_write(srv, __FILE__, __LINE__, "s",
+				"(either gdbm or libmemcache) and pcre are require, but were not found, aborting");
+		return HANDLER_ERROR;
+#endif
+	}
+
+	return HANDLER_GO_ON;
+}
+
+#define PATCH(x) \
+	p->conf.x = s->x;
+static int mod_trigger_b4_dl_patch_connection(server *srv, connection *con, plugin_data *p) {
+	size_t i, j;
+	plugin_config *s = p->config_storage[0];
+
+#if defined(HAVE_GDBM)
+	PATCH(db);
+#endif
+#if defined(HAVE_PCRE_H)
+	PATCH(download_regex);
+	PATCH(trigger_regex);
+#endif
+	PATCH(trigger_timeout);
+	PATCH(deny_url);
+	PATCH(mc_namespace);
+	PATCH(debug);
+#if defined(HAVE_MEMCACHE_H)
+	PATCH(mc);
+#endif
+
+	/* skip the first, the global context */
+	for (i = 1; i < srv->config_context->used; i++) {
+		data_config *dc = (data_config *)srv->config_context->data[i];
+		s = p->config_storage[i];
+
+		/* condition didn't match */
+		if (!config_check_cond(srv, con, dc)) continue;
+
+		/* merge config */
+		for (j = 0; j < dc->value->used; j++) {
+			data_unset *du = dc->value->data[j];
+
+			if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.download-url"))) {
+#if defined(HAVE_PCRE_H)
+				PATCH(download_regex);
+#endif
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.trigger-url"))) {
+# if defined(HAVE_PCRE_H)
+				PATCH(trigger_regex);
+# endif
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.gdbm-filename"))) {
+#if defined(HAVE_GDBM_H)
+				PATCH(db);
+#endif
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.trigger-timeout"))) {
+				PATCH(trigger_timeout);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.debug"))) {
+				PATCH(debug);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.deny-url"))) {
+				PATCH(deny_url);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.memcache-namespace"))) {
+				PATCH(mc_namespace);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.memcache-hosts"))) {
+#if defined(HAVE_MEMCACHE_H)
+				PATCH(mc);
+#endif
+			}
+		}
+	}
+
+	return 0;
+}
+#undef PATCH
+
+URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) {
+	plugin_data *p = p_d;
+	const char *remote_ip;
+	data_string *ds;
+
+#if defined(HAVE_PCRE_H)
+	int n;
+# define N 10
+	int ovec[N * 3];
+
+	if (con->uri.path->used == 0) return HANDLER_GO_ON;
+
+	mod_trigger_b4_dl_patch_connection(srv, con, p);
+
+	if (!p->conf.trigger_regex || !p->conf.download_regex) return HANDLER_GO_ON;
+
+# if !defined(HAVE_GDBM_H) && !defined(HAVE_MEMCACHE_H)
+	return HANDLER_GO_ON;
+# elif defined(HAVE_GDBM_H) && defined(HAVE_MEMCACHE_H)
+	if (!p->conf.db && !p->conf.mc) return HANDLER_GO_ON;
+	if (p->conf.db && p->conf.mc) {
+		/* can't decide which one */
+
+		return HANDLER_GO_ON;
+	}
+# elif defined(HAVE_GDBM_H)
+	if (!p->conf.db) return HANDLER_GO_ON;
+# else
+	if (!p->conf.mc) return HANDLER_GO_ON;
+# endif
+
+	if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "X-Forwarded-For"))) {
+		/* X-Forwarded-For contains the ip behind the proxy */
+
+		remote_ip = ds->value->ptr;
+
+		/* memcache can't handle spaces */
+	} else {
+		remote_ip = inet_ntop_cache_get_ip(srv, &(con->dst_addr));
+	}
+
+	if (p->conf.debug) {
+		log_error_write(srv, __FILE__, __LINE__, "ss", "(debug) remote-ip:", remote_ip);
+	}
+
+	/* check if URL is a trigger -> insert IP into DB */
+	if ((n = pcre_exec(p->conf.trigger_regex, NULL, con->uri.path->ptr, con->uri.path->used - 1, 0, 0, ovec, 3 * N)) < 0) {
+		if (n != PCRE_ERROR_NOMATCH) {
+			log_error_write(srv, __FILE__, __LINE__, "sd",
+					"execution error while matching:", n);
+
+			return HANDLER_ERROR;
+		}
+	} else {
+# if defined(HAVE_GDBM_H)
+		if (p->conf.db) {
+			/* the trigger matched */
+			datum key, val;
+
+			key.dptr = (char *)remote_ip;
+			key.dsize = strlen(remote_ip);
+
+			val.dptr = (char *)&(srv->cur_ts);
+			val.dsize = sizeof(srv->cur_ts);
+
+			if (0 != gdbm_store(p->conf.db, key, val, GDBM_REPLACE)) {
+				log_error_write(srv, __FILE__, __LINE__, "s",
+						"insert failed");
+			}
+		}
+# endif
+# if defined(HAVE_MEMCACHE_H)
+		if (p->conf.mc) {
+			size_t i;
+			buffer_copy_string_buffer(p->tmp_buf, p->conf.mc_namespace);
+			buffer_append_string(p->tmp_buf, remote_ip);
+
+			for (i = 0; i < p->tmp_buf->used - 1; i++) {
+				if (p->tmp_buf->ptr[i] == ' ') p->tmp_buf->ptr[i] = '-';
+			}
+
+			if (p->conf.debug) {
+				log_error_write(srv, __FILE__, __LINE__, "sb", "(debug) triggered IP:", p->tmp_buf);
+			}
+
+			if (0 != mc_set(p->conf.mc,
+					CONST_BUF_LEN(p->tmp_buf),
+					(char *)&(srv->cur_ts), sizeof(srv->cur_ts),
+					p->conf.trigger_timeout, 0)) {
+				log_error_write(srv, __FILE__, __LINE__, "s",
+						"insert failed");
+			}
+		}
+# endif
+	}
+
+	/* check if URL is a download -> check IP in DB, update timestamp */
+	if ((n = pcre_exec(p->conf.download_regex, NULL, con->uri.path->ptr, con->uri.path->used - 1, 0, 0, ovec, 3 * N)) < 0) {
+		if (n != PCRE_ERROR_NOMATCH) {
+			log_error_write(srv, __FILE__, __LINE__, "sd",
+					"execution error while matching: ", n);
+			return HANDLER_ERROR;
+		}
+	} else {
+		/* the download uri matched */
+# if defined(HAVE_GDBM_H)
+		if (p->conf.db) {
+			datum key, val;
+			time_t last_hit;
+
+			key.dptr = (char *)remote_ip;
+			key.dsize = strlen(remote_ip);
+
+			val = gdbm_fetch(p->conf.db, key);
+
+			if (val.dptr == NULL) {
+				/* not found, redirect */
+
+				response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url));
+
+				con->http_status = 307;
+
+				return HANDLER_FINISHED;
+			}
+
+			last_hit = *(time_t *)(val.dptr);
+
+			free(val.dptr);
+
+			if (srv->cur_ts - last_hit > p->conf.trigger_timeout) {
+				/* found, but timeout, redirect */
+
+				response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url));
+				con->http_status = 307;
+
+				if (p->conf.db) {
+					if (0 != gdbm_delete(p->conf.db, key)) {
+						log_error_write(srv, __FILE__, __LINE__, "s",
+								"delete failed");
+					}
+				}
+
+				return HANDLER_FINISHED;
+			}
+
+			val.dptr = (char *)&(srv->cur_ts);
+			val.dsize = sizeof(srv->cur_ts);
+
+			if (0 != gdbm_store(p->conf.db, key, val, GDBM_REPLACE)) {
+				log_error_write(srv, __FILE__, __LINE__, "s",
+						"insert failed");
+			}
+		}
+# endif
+
+# if defined(HAVE_MEMCACHE_H)
+		if (p->conf.mc) {
+			void *r;
+			size_t i;
+
+			buffer_copy_string_buffer(p->tmp_buf, p->conf.mc_namespace);
+			buffer_append_string(p->tmp_buf, remote_ip);
+
+			for (i = 0; i < p->tmp_buf->used - 1; i++) {
+				if (p->tmp_buf->ptr[i] == ' ') p->tmp_buf->ptr[i] = '-';
+			}
+
+			if (p->conf.debug) {
+				log_error_write(srv, __FILE__, __LINE__, "sb", "(debug) checking IP:", p->tmp_buf);
+			}
+
+			/**
+			 *
+			 * memcached is do expiration for us, as long as we can fetch it every thing is ok
+			 * and the timestamp is updated
+			 *
+			 */
+			if (NULL == (r = mc_aget(p->conf.mc,
+						 CONST_BUF_LEN(p->tmp_buf)
+						 ))) {
+
+				response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url));
+
+				con->http_status = 307;
+
+				return HANDLER_FINISHED;
+			}
+
+			free(r);
+
+			/* set a new timeout */
+			if (0 != mc_set(p->conf.mc,
+					CONST_BUF_LEN(p->tmp_buf),
+					(char *)&(srv->cur_ts), sizeof(srv->cur_ts),
+					p->conf.trigger_timeout, 0)) {
+				log_error_write(srv, __FILE__, __LINE__, "s",
+						"insert failed");
+			}
+		}
+# endif
+	}
+
+#else
+	UNUSED(srv);
+	UNUSED(con);
+	UNUSED(p_d);
+#endif
+
+	return HANDLER_GO_ON;
+}
+
+#if defined(HAVE_GDBM_H)
+TRIGGER_FUNC(mod_trigger_b4_dl_handle_trigger) {
+	plugin_data *p = p_d;
+	size_t i;
+
+	/* check DB each minute */
+	if (srv->cur_ts % 60 != 0) return HANDLER_GO_ON;
+
+	/* cleanup */
+	for (i = 0; i < srv->config_context->used; i++) {
+		plugin_config *s = p->config_storage[i];
+		datum key, val, okey;
+
+		if (!s->db) continue;
+
+		okey.dptr = NULL;
+
+		/* according to the manual this loop + delete does delete all entries on its way
+		 *
+		 * we don't care as the next round will remove them. We don't have to perfect here.
+		 */
+		for (key = gdbm_firstkey(s->db); key.dptr; key = gdbm_nextkey(s->db, okey)) {
+			time_t last_hit;
+			if (okey.dptr) {
+				free(okey.dptr);
+				okey.dptr = NULL;
+			}
+
+			val = gdbm_fetch(s->db, key);
+
+			last_hit = *(time_t *)(val.dptr);
+
+			free(val.dptr);
+
+			if (srv->cur_ts - last_hit > s->trigger_timeout) {
+				gdbm_delete(s->db, key);
+			}
+
+			okey = key;
+		}
+		if (okey.dptr) free(okey.dptr);
+
+		/* reorg once a day */
+		if ((srv->cur_ts % (60 * 60 * 24) != 0)) gdbm_reorganize(s->db);
+	}
+	return HANDLER_GO_ON;
+}
+#endif
+
+/* this function is called at dlopen() time and inits the callbacks */
+
+int mod_trigger_b4_dl_plugin_init(plugin *p) {
+	p->version     = LIGHTTPD_VERSION_ID;
+	p->name        = buffer_init_string("trigger_b4_dl");
+
+	p->init        = mod_trigger_b4_dl_init;
+	p->handle_uri_clean  = mod_trigger_b4_dl_uri_handler;
+	p->set_defaults  = mod_trigger_b4_dl_set_defaults;
+#if defined(HAVE_GDBM_H)
+	p->handle_trigger  = mod_trigger_b4_dl_handle_trigger;
+#endif
+	p->cleanup     = mod_trigger_b4_dl_free;
+
+	p->data        = NULL;
+
+	return 0;
+}

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_userdir.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_userdir.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_userdir.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_userdir.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,310 @@
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base.h"
+#include "log.h"
+#include "buffer.h"
+
+#include "response.h"
+
+#include "plugin.h"
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+/* plugin config for all request/connections */
+typedef struct {
+	array *exclude_user;
+	array *include_user;
+	buffer *path;
+	buffer *basepath;
+	unsigned short letterhomes;
+} plugin_config;
+
+typedef struct {
+	PLUGIN_DATA;
+
+	buffer *username;
+	buffer *temp_path;
+
+	plugin_config **config_storage;
+
+	plugin_config conf;
+} plugin_data;
+
+/* init the plugin data */
+INIT_FUNC(mod_userdir_init) {
+	plugin_data *p;
+
+	p = calloc(1, sizeof(*p));
+
+	p->username = buffer_init();
+	p->temp_path = buffer_init();
+
+	return p;
+}
+
+/* detroy the plugin data */
+FREE_FUNC(mod_userdir_free) {
+	plugin_data *p = p_d;
+
+	if (!p) return HANDLER_GO_ON;
+
+	if (p->config_storage) {
+		size_t i;
+
+		for (i = 0; i < srv->config_context->used; i++) {
+			plugin_config *s = p->config_storage[i];
+
+			array_free(s->include_user);
+			array_free(s->exclude_user);
+			buffer_free(s->path);
+			buffer_free(s->basepath);
+
+			free(s);
+		}
+		free(p->config_storage);
+	}
+
+	buffer_free(p->username);
+	buffer_free(p->temp_path);
+
+	free(p);
+
+	return HANDLER_GO_ON;
+}
+
+/* handle plugin config and check values */
+
+SETDEFAULTS_FUNC(mod_userdir_set_defaults) {
+	plugin_data *p = p_d;
+	size_t i;
+
+	config_values_t cv[] = {
+		{ "userdir.path",               NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
+		{ "userdir.exclude-user",       NULL, T_CONFIG_ARRAY,  T_CONFIG_SCOPE_CONNECTION },       /* 1 */
+		{ "userdir.include-user",       NULL, T_CONFIG_ARRAY,  T_CONFIG_SCOPE_CONNECTION },       /* 2 */
+		{ "userdir.basepath",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
+		{ "userdir.letterhomes",	NULL, T_CONFIG_BOOLEAN,T_CONFIG_SCOPE_CONNECTION },	  /* 4 */
+		{ NULL,                         NULL, T_CONFIG_UNSET,  T_CONFIG_SCOPE_UNSET }
+	};
+
+	if (!p) return HANDLER_ERROR;
+
+	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
+
+	for (i = 0; i < srv->config_context->used; i++) {
+		plugin_config *s;
+
+		s = calloc(1, sizeof(plugin_config));
+		s->exclude_user = array_init();
+		s->include_user = array_init();
+		s->path = buffer_init();
+		s->basepath = buffer_init();
+		s->letterhomes = 0;
+
+		cv[0].destination = s->path;
+		cv[1].destination = s->exclude_user;
+		cv[2].destination = s->include_user;
+		cv[3].destination = s->basepath;
+		cv[4].destination = &(s->letterhomes);
+
+		p->config_storage[i] = s;
+
+		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+			return HANDLER_ERROR;
+		}
+	}
+
+	return HANDLER_GO_ON;
+}
+
+#define PATCH(x) \
+	p->conf.x = s->x;
+static int mod_userdir_patch_connection(server *srv, connection *con, plugin_data *p) {
+	size_t i, j;
+	plugin_config *s = p->config_storage[0];
+
+	PATCH(path);
+	PATCH(exclude_user);
+	PATCH(include_user);
+	PATCH(basepath);
+	PATCH(letterhomes);
+
+	/* skip the first, the global context */
+	for (i = 1; i < srv->config_context->used; i++) {
+		data_config *dc = (data_config *)srv->config_context->data[i];
+		s = p->config_storage[i];
+
+		/* condition didn't match */
+		if (!config_check_cond(srv, con, dc)) continue;
+
+		/* merge config */
+		for (j = 0; j < dc->value->used; j++) {
+			data_unset *du = dc->value->data[j];
+
+			if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.path"))) {
+				PATCH(path);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.exclude-user"))) {
+				PATCH(exclude_user);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.include-user"))) {
+				PATCH(include_user);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.basepath"))) {
+				PATCH(basepath);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.letterhomes"))) {
+				PATCH(letterhomes);
+			}
+		}
+	}
+
+	return 0;
+}
+#undef PATCH
+
+URIHANDLER_FUNC(mod_userdir_docroot_handler) {
+	plugin_data *p = p_d;
+	int uri_len;
+	size_t k;
+	char *rel_url;
+#ifdef HAVE_PWD_H
+	struct passwd *pwd = NULL;
+#endif
+
+	if (con->uri.path->used == 0) return HANDLER_GO_ON;
+
+	mod_userdir_patch_connection(srv, con, p);
+
+	/* enforce the userdir.path to be set in the config, ugly fix for #1587;
+	 * should be replaced with a clean .enabled option in 1.5
+	 */
+	if (p->conf.path->used == 0) return HANDLER_GO_ON;
+
+	uri_len = con->uri.path->used - 1;
+
+	/* /~user/foo.html -> /home/user/public_html/foo.html */
+
+	if (con->uri.path->ptr[0] != '/' ||
+	    con->uri.path->ptr[1] != '~') return HANDLER_GO_ON;
+
+	if (NULL == (rel_url = strchr(con->uri.path->ptr + 2, '/'))) {
+		/* / is missing -> redirect to .../ as we are a user - DIRECTORY ! :) */
+		http_response_redirect_to_directory(srv, con);
+
+		return HANDLER_FINISHED;
+	}
+
+	/* /~/ is a empty username, catch it directly */
+	if (0 == rel_url - (con->uri.path->ptr + 2)) {
+		return HANDLER_GO_ON;
+	}
+
+	buffer_copy_string_len(p->username, con->uri.path->ptr + 2, rel_url - (con->uri.path->ptr + 2));
+
+	if (buffer_is_empty(p->conf.basepath)
+#ifdef HAVE_PWD_H
+	    && NULL == (pwd = getpwnam(p->username->ptr))
+#endif
+	    ) {
+		/* user not found */
+		return HANDLER_GO_ON;
+	}
+
+
+	for (k = 0; k < p->conf.exclude_user->used; k++) {
+		data_string *ds = (data_string *)p->conf.exclude_user->data[k];
+
+		if (buffer_is_equal(ds->value, p->username)) {
+			/* user in exclude list */
+			return HANDLER_GO_ON;
+		}
+	}
+
+	if (p->conf.include_user->used) {
+		int found_user = 0;
+		for (k = 0; k < p->conf.include_user->used; k++) {
+			data_string *ds = (data_string *)p->conf.include_user->data[k];
+
+			if (buffer_is_equal(ds->value, p->username)) {
+				/* user in include list */
+				found_user = 1;
+				break;
+			}
+		}
+
+		if (!found_user) return HANDLER_GO_ON;
+	}
+
+	/* we build the physical path */
+
+	if (buffer_is_empty(p->conf.basepath)) {
+#ifdef HAVE_PWD_H
+		buffer_copy_string(p->temp_path, pwd->pw_dir);
+#endif
+	} else {
+		char *cp;
+		/* check if the username is valid
+		 * a request for /~../ should lead to a directory traversal
+		 * limiting to [-_a-z0-9.] should fix it */
+
+		for (cp = p->username->ptr; *cp; cp++) {
+			char c = *cp;
+
+			if (!(c == '-' ||
+			      c == '_' ||
+			      c == '.' ||
+			      (c >= 'a' && c <= 'z') ||
+			      (c >= 'A' && c <= 'Z') ||
+			      (c >= '0' && c <= '9'))) {
+
+				return HANDLER_GO_ON;
+			}
+		}
+
+		buffer_copy_string_buffer(p->temp_path, p->conf.basepath);
+		BUFFER_APPEND_SLASH(p->temp_path);
+		if (p->conf.letterhomes) {
+			buffer_append_string_len(p->temp_path, p->username->ptr, 1);
+			BUFFER_APPEND_SLASH(p->temp_path);
+		}
+		buffer_append_string_buffer(p->temp_path, p->username);
+	}
+	BUFFER_APPEND_SLASH(p->temp_path);
+	buffer_append_string_buffer(p->temp_path, p->conf.path);
+
+	if (buffer_is_empty(p->conf.basepath)) {
+		struct stat st;
+		int ret;
+
+		ret = stat(p->temp_path->ptr, &st);
+		if (ret < 0 || S_ISDIR(st.st_mode) != 1) {
+			return HANDLER_GO_ON;
+		}
+	}
+
+	BUFFER_APPEND_SLASH(p->temp_path);
+	buffer_append_string(p->temp_path, rel_url + 1); /* skip the / */
+	buffer_copy_string_buffer(con->physical.path, p->temp_path);
+
+	buffer_reset(p->temp_path);
+
+	return HANDLER_GO_ON;
+}
+
+/* this function is called at dlopen() time and inits the callbacks */
+
+int mod_userdir_plugin_init(plugin *p) {
+	p->version     = LIGHTTPD_VERSION_ID;
+	p->name        = buffer_init_string("userdir");
+
+	p->init           = mod_userdir_init;
+	p->handle_physical = mod_userdir_docroot_handler;
+	p->set_defaults   = mod_userdir_set_defaults;
+	p->cleanup        = mod_userdir_free;
+
+	p->data        = NULL;
+
+	return 0;
+}

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_usertrack.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_usertrack.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_usertrack.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_usertrack.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,270 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base.h"
+#include "log.h"
+#include "buffer.h"
+
+#include "plugin.h"
+
+#ifdef USE_OPENSSL
+# include <openssl/md5.h>
+#else
+# include "md5.h"
+#endif
+
+/* plugin config for all request/connections */
+
+typedef struct {
+	buffer *cookie_name;
+	buffer *cookie_domain;
+	unsigned short cookie_max_age;
+} plugin_config;
+
+typedef struct {
+	PLUGIN_DATA;
+
+	plugin_config **config_storage;
+
+	plugin_config conf;
+} plugin_data;
+
+/* init the plugin data */
+INIT_FUNC(mod_usertrack_init) {
+	plugin_data *p;
+
+	p = calloc(1, sizeof(*p));
+
+	return p;
+}
+
+/* detroy the plugin data */
+FREE_FUNC(mod_usertrack_free) {
+	plugin_data *p = p_d;
+
+	UNUSED(srv);
+
+	if (!p) return HANDLER_GO_ON;
+
+	if (p->config_storage) {
+		size_t i;
+		for (i = 0; i < srv->config_context->used; i++) {
+			plugin_config *s = p->config_storage[i];
+
+			buffer_free(s->cookie_name);
+			buffer_free(s->cookie_domain);
+
+			free(s);
+		}
+		free(p->config_storage);
+	}
+
+	free(p);
+
+	return HANDLER_GO_ON;
+}
+
+/* handle plugin config and check values */
+
+SETDEFAULTS_FUNC(mod_usertrack_set_defaults) {
+	plugin_data *p = p_d;
+	size_t i = 0;
+
+	config_values_t cv[] = {
+		{ "usertrack.cookie-name",       NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
+		{ "usertrack.cookie-max-age",    NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 1 */
+		{ "usertrack.cookie-domain",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
+
+		{ "usertrack.cookiename",        NULL, T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_CONNECTION },
+		{ NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
+	};
+
+	if (!p) return HANDLER_ERROR;
+
+	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
+
+	for (i = 0; i < srv->config_context->used; i++) {
+		plugin_config *s;
+
+		s = calloc(1, sizeof(plugin_config));
+		s->cookie_name    = buffer_init();
+		s->cookie_domain  = buffer_init();
+		s->cookie_max_age = 0;
+
+		cv[0].destination = s->cookie_name;
+		cv[1].destination = &(s->cookie_max_age);
+		cv[2].destination = s->cookie_domain;
+
+		p->config_storage[i] = s;
+
+		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+			return HANDLER_ERROR;
+		}
+
+		if (buffer_is_empty(s->cookie_name)) {
+			buffer_copy_string(s->cookie_name, "TRACKID");
+		} else {
+			size_t j;
+			for (j = 0; j < s->cookie_name->used - 1; j++) {
+				char c = s->cookie_name->ptr[j] | 32;
+				if (c < 'a' || c > 'z') {
+					log_error_write(srv, __FILE__, __LINE__, "sb",
+							"invalid character in usertrack.cookie-name:",
+							s->cookie_name);
+
+					return HANDLER_ERROR;
+				}
+			}
+		}
+
+		if (!buffer_is_empty(s->cookie_domain)) {
+			size_t j;
+			for (j = 0; j < s->cookie_domain->used - 1; j++) {
+				char c = s->cookie_domain->ptr[j];
+				if (c <= 32 || c >= 127 || c == '"' || c == '\\') {
+					log_error_write(srv, __FILE__, __LINE__, "sb",
+							"invalid character in usertrack.cookie-domain:",
+							s->cookie_domain);
+
+					return HANDLER_ERROR;
+				}
+			}
+		}
+	}
+
+	return HANDLER_GO_ON;
+}
+
+#define PATCH(x) \
+	p->conf.x = s->x;
+static int mod_usertrack_patch_connection(server *srv, connection *con, plugin_data *p) {
+	size_t i, j;
+	plugin_config *s = p->config_storage[0];
+
+	PATCH(cookie_name);
+	PATCH(cookie_domain);
+	PATCH(cookie_max_age);
+
+	/* skip the first, the global context */
+	for (i = 1; i < srv->config_context->used; i++) {
+		data_config *dc = (data_config *)srv->config_context->data[i];
+		s = p->config_storage[i];
+
+		/* condition didn't match */
+		if (!config_check_cond(srv, con, dc)) continue;
+
+		/* merge config */
+		for (j = 0; j < dc->value->used; j++) {
+			data_unset *du = dc->value->data[j];
+
+			if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-name"))) {
+				PATCH(cookie_name);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-max-age"))) {
+				PATCH(cookie_max_age);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-domain"))) {
+				PATCH(cookie_domain);
+			}
+		}
+	}
+
+	return 0;
+}
+#undef PATCH
+
+URIHANDLER_FUNC(mod_usertrack_uri_handler) {
+	plugin_data *p = p_d;
+	data_string *ds;
+	unsigned char h[16];
+	MD5_CTX Md5Ctx;
+	char hh[32];
+
+	if (con->uri.path->used == 0) return HANDLER_GO_ON;
+
+	mod_usertrack_patch_connection(srv, con, p);
+
+	if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
+		char *g;
+		/* we have a cookie, does it contain a valid name ? */
+
+		/* parse the cookie
+		 *
+		 * check for cookiename + (WS | '=')
+		 *
+		 */
+
+		if (NULL != (g = strstr(ds->value->ptr, p->conf.cookie_name->ptr))) {
+			char *nc;
+
+			/* skip WS */
+			for (nc = g + p->conf.cookie_name->used-1; *nc == ' ' || *nc == '\t'; nc++);
+
+			if (*nc == '=') {
+				/* ok, found the key of our own cookie */
+
+				if (strlen(nc) > 32) {
+					/* i'm lazy */
+					return HANDLER_GO_ON;
+				}
+			}
+		}
+	}
+
+	/* set a cookie */
+	if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
+		ds = data_response_init();
+	}
+	buffer_copy_string(ds->key, "Set-Cookie");
+	buffer_copy_string_buffer(ds->value, p->conf.cookie_name);
+	buffer_append_string(ds->value, "=");
+
+
+	/* taken from mod_auth.c */
+
+	/* generate shared-secret */
+	MD5_Init(&Md5Ctx);
+	MD5_Update(&Md5Ctx, (unsigned char *)con->uri.path->ptr, con->uri.path->used - 1);
+	MD5_Update(&Md5Ctx, (unsigned char *)"+", 1);
+
+	/* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
+	LI_ltostr(hh, srv->cur_ts);
+	MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
+	LI_ltostr(hh, rand());
+	MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
+
+	MD5_Final(h, &Md5Ctx);
+
+	buffer_append_string_encoded(ds->value, (char *)h, 16, ENCODING_HEX);
+	buffer_append_string(ds->value, "; Path=/");
+	buffer_append_string(ds->value, "; Version=1");
+
+	if (!buffer_is_empty(p->conf.cookie_domain)) {
+		buffer_append_string(ds->value, "; Domain=");
+		buffer_append_string_encoded(ds->value, CONST_BUF_LEN(p->conf.cookie_domain), ENCODING_REL_URI);
+	}
+
+	if (p->conf.cookie_max_age) {
+		buffer_append_string(ds->value, "; max-age=");
+		buffer_append_long(ds->value, p->conf.cookie_max_age);
+	}
+
+	array_insert_unique(con->response.headers, (data_unset *)ds);
+
+	return HANDLER_GO_ON;
+}
+
+/* this function is called at dlopen() time and inits the callbacks */
+
+int mod_usertrack_plugin_init(plugin *p) {
+	p->version     = LIGHTTPD_VERSION_ID;
+	p->name        = buffer_init_string("usertrack");
+
+	p->init        = mod_usertrack_init;
+	p->handle_uri_clean  = mod_usertrack_uri_handler;
+	p->set_defaults  = mod_usertrack_set_defaults;
+	p->cleanup     = mod_usertrack_free;
+
+	p->data        = NULL;
+
+	return 0;
+}