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 = '↑';\n"
+ " span.setAttribute('sortdir','up');\n"
+ " rows.reverse();\n"
+ " } else {\n"
+ " span.innerHTML = '↓';\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;
+}