File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_trigger_b4_dl.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:20:06 2014 UTC (10 years, 1 month ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, HEAD
lighttpd 1.4.35

    1: #include "base.h"
    2: #include "log.h"
    3: #include "buffer.h"
    4: 
    5: #include "plugin.h"
    6: #include "response.h"
    7: #include "inet_ntop_cache.h"
    8: 
    9: #include <ctype.h>
   10: #include <stdlib.h>
   11: #include <fcntl.h>
   12: #include <string.h>
   13: 
   14: #if defined(HAVE_GDBM_H)
   15: # include <gdbm.h>
   16: #endif
   17: 
   18: #if defined(HAVE_PCRE_H)
   19: # include <pcre.h>
   20: #endif
   21: 
   22: #if defined(HAVE_MEMCACHE_H)
   23: # include <memcache.h>
   24: #endif
   25: 
   26: /**
   27:  * this is a trigger_b4_dl for a lighttpd plugin
   28:  *
   29:  */
   30: 
   31: /* plugin config for all request/connections */
   32: 
   33: typedef struct {
   34: 	buffer *db_filename;
   35: 
   36: 	buffer *trigger_url;
   37: 	buffer *download_url;
   38: 	buffer *deny_url;
   39: 
   40: 	array  *mc_hosts;
   41: 	buffer *mc_namespace;
   42: #if defined(HAVE_PCRE_H)
   43: 	pcre *trigger_regex;
   44: 	pcre *download_regex;
   45: #endif
   46: #if defined(HAVE_GDBM_H)
   47: 	GDBM_FILE db;
   48: #endif
   49: 
   50: #if defined(HAVE_MEMCACHE_H)
   51: 	struct memcache *mc;
   52: #endif
   53: 
   54: 	unsigned short trigger_timeout;
   55: 	unsigned short debug;
   56: } plugin_config;
   57: 
   58: typedef struct {
   59: 	PLUGIN_DATA;
   60: 
   61: 	buffer *tmp_buf;
   62: 
   63: 	plugin_config **config_storage;
   64: 
   65: 	plugin_config conf;
   66: } plugin_data;
   67: 
   68: /* init the plugin data */
   69: INIT_FUNC(mod_trigger_b4_dl_init) {
   70: 	plugin_data *p;
   71: 
   72: 	p = calloc(1, sizeof(*p));
   73: 
   74: 	p->tmp_buf = buffer_init();
   75: 
   76: 	return p;
   77: }
   78: 
   79: /* detroy the plugin data */
   80: FREE_FUNC(mod_trigger_b4_dl_free) {
   81: 	plugin_data *p = p_d;
   82: 
   83: 	UNUSED(srv);
   84: 
   85: 	if (!p) return HANDLER_GO_ON;
   86: 
   87: 	if (p->config_storage) {
   88: 		size_t i;
   89: 		for (i = 0; i < srv->config_context->used; i++) {
   90: 			plugin_config *s = p->config_storage[i];
   91: 
   92: 			if (!s) continue;
   93: 
   94: 			buffer_free(s->db_filename);
   95: 			buffer_free(s->download_url);
   96: 			buffer_free(s->trigger_url);
   97: 			buffer_free(s->deny_url);
   98: 
   99: 			buffer_free(s->mc_namespace);
  100: 			array_free(s->mc_hosts);
  101: 
  102: #if defined(HAVE_PCRE_H)
  103: 			if (s->trigger_regex) pcre_free(s->trigger_regex);
  104: 			if (s->download_regex) pcre_free(s->download_regex);
  105: #endif
  106: #if defined(HAVE_GDBM_H)
  107: 			if (s->db) gdbm_close(s->db);
  108: #endif
  109: #if defined(HAVE_MEMCACHE_H)
  110: 			if (s->mc) mc_free(s->mc);
  111: #endif
  112: 
  113: 			free(s);
  114: 		}
  115: 		free(p->config_storage);
  116: 	}
  117: 
  118: 	buffer_free(p->tmp_buf);
  119: 
  120: 	free(p);
  121: 
  122: 	return HANDLER_GO_ON;
  123: }
  124: 
  125: /* handle plugin config and check values */
  126: 
  127: SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) {
  128: 	plugin_data *p = p_d;
  129: 	size_t i = 0;
  130: 
  131: 
  132: 	config_values_t cv[] = {
  133: 		{ "trigger-before-download.gdbm-filename",   NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
  134: 		{ "trigger-before-download.trigger-url",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
  135: 		{ "trigger-before-download.download-url",    NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
  136: 		{ "trigger-before-download.deny-url",        NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
  137: 		{ "trigger-before-download.trigger-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 4 */
  138: 		{ "trigger-before-download.memcache-hosts",  NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },        /* 5 */
  139: 		{ "trigger-before-download.memcache-namespace", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },    /* 6 */
  140: 		{ "trigger-before-download.debug",           NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },      /* 7 */
  141: 		{ NULL,                        NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
  142: 	};
  143: 
  144: 	if (!p) return HANDLER_ERROR;
  145: 
  146: 	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
  147: 
  148: 	for (i = 0; i < srv->config_context->used; i++) {
  149: 		plugin_config *s;
  150: #if defined(HAVE_PCRE_H)
  151: 		const char *errptr;
  152: 		int erroff;
  153: #endif
  154: 
  155: 		s = calloc(1, sizeof(plugin_config));
  156: 		s->db_filename    = buffer_init();
  157: 		s->download_url   = buffer_init();
  158: 		s->trigger_url    = buffer_init();
  159: 		s->deny_url       = buffer_init();
  160: 		s->mc_hosts       = array_init();
  161: 		s->mc_namespace   = buffer_init();
  162: 
  163: 		cv[0].destination = s->db_filename;
  164: 		cv[1].destination = s->trigger_url;
  165: 		cv[2].destination = s->download_url;
  166: 		cv[3].destination = s->deny_url;
  167: 		cv[4].destination = &(s->trigger_timeout);
  168: 		cv[5].destination = s->mc_hosts;
  169: 		cv[6].destination = s->mc_namespace;
  170: 		cv[7].destination = &(s->debug);
  171: 
  172: 		p->config_storage[i] = s;
  173: 
  174: 		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
  175: 			return HANDLER_ERROR;
  176: 		}
  177: #if defined(HAVE_GDBM_H)
  178: 		if (!buffer_is_empty(s->db_filename)) {
  179: 			if (NULL == (s->db = gdbm_open(s->db_filename->ptr, 4096, GDBM_WRCREAT | GDBM_NOLOCK, S_IRUSR | S_IWUSR, 0))) {
  180: 				log_error_write(srv, __FILE__, __LINE__, "s",
  181: 						"gdbm-open failed");
  182: 				return HANDLER_ERROR;
  183: 			}
  184: 			fd_close_on_exec(gdbm_fdesc(s->db));
  185: 		}
  186: #endif
  187: #if defined(HAVE_PCRE_H)
  188: 		if (!buffer_is_empty(s->download_url)) {
  189: 			if (NULL == (s->download_regex = pcre_compile(s->download_url->ptr,
  190: 								      0, &errptr, &erroff, NULL))) {
  191: 
  192: 				log_error_write(srv, __FILE__, __LINE__, "sbss",
  193: 						"compiling regex for download-url failed:",
  194: 						s->download_url, "pos:", erroff);
  195: 				return HANDLER_ERROR;
  196: 			}
  197: 		}
  198: 
  199: 		if (!buffer_is_empty(s->trigger_url)) {
  200: 			if (NULL == (s->trigger_regex = pcre_compile(s->trigger_url->ptr,
  201: 								     0, &errptr, &erroff, NULL))) {
  202: 
  203: 				log_error_write(srv, __FILE__, __LINE__, "sbss",
  204: 						"compiling regex for trigger-url failed:",
  205: 						s->trigger_url, "pos:", erroff);
  206: 
  207: 				return HANDLER_ERROR;
  208: 			}
  209: 		}
  210: #endif
  211: 
  212: 		if (s->mc_hosts->used) {
  213: #if defined(HAVE_MEMCACHE_H)
  214: 			size_t k;
  215: 			s->mc = mc_new();
  216: 
  217: 			for (k = 0; k < s->mc_hosts->used; k++) {
  218: 				data_string *ds = (data_string *)s->mc_hosts->data[k];
  219: 
  220: 				if (0 != mc_server_add4(s->mc, ds->value->ptr)) {
  221: 					log_error_write(srv, __FILE__, __LINE__, "sb",
  222: 							"connection to host failed:",
  223: 							ds->value);
  224: 
  225: 					return HANDLER_ERROR;
  226: 				}
  227: 			}
  228: #else
  229: 			log_error_write(srv, __FILE__, __LINE__, "s",
  230: 					"memcache support is not compiled in but trigger-before-download.memcache-hosts is set, aborting");
  231: 			return HANDLER_ERROR;
  232: #endif
  233: 		}
  234: 
  235: 
  236: #if (!defined(HAVE_GDBM_H) && !defined(HAVE_MEMCACHE_H)) || !defined(HAVE_PCRE_H)
  237: 		log_error_write(srv, __FILE__, __LINE__, "s",
  238: 				"(either gdbm or libmemcache) and pcre are require, but were not found, aborting");
  239: 		return HANDLER_ERROR;
  240: #endif
  241: 	}
  242: 
  243: 	return HANDLER_GO_ON;
  244: }
  245: 
  246: #define PATCH(x) \
  247: 	p->conf.x = s->x;
  248: static int mod_trigger_b4_dl_patch_connection(server *srv, connection *con, plugin_data *p) {
  249: 	size_t i, j;
  250: 	plugin_config *s = p->config_storage[0];
  251: 
  252: #if defined(HAVE_GDBM)
  253: 	PATCH(db);
  254: #endif
  255: #if defined(HAVE_PCRE_H)
  256: 	PATCH(download_regex);
  257: 	PATCH(trigger_regex);
  258: #endif
  259: 	PATCH(trigger_timeout);
  260: 	PATCH(deny_url);
  261: 	PATCH(mc_namespace);
  262: 	PATCH(debug);
  263: #if defined(HAVE_MEMCACHE_H)
  264: 	PATCH(mc);
  265: #endif
  266: 
  267: 	/* skip the first, the global context */
  268: 	for (i = 1; i < srv->config_context->used; i++) {
  269: 		data_config *dc = (data_config *)srv->config_context->data[i];
  270: 		s = p->config_storage[i];
  271: 
  272: 		/* condition didn't match */
  273: 		if (!config_check_cond(srv, con, dc)) continue;
  274: 
  275: 		/* merge config */
  276: 		for (j = 0; j < dc->value->used; j++) {
  277: 			data_unset *du = dc->value->data[j];
  278: 
  279: 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.download-url"))) {
  280: #if defined(HAVE_PCRE_H)
  281: 				PATCH(download_regex);
  282: #endif
  283: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.trigger-url"))) {
  284: # if defined(HAVE_PCRE_H)
  285: 				PATCH(trigger_regex);
  286: # endif
  287: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.gdbm-filename"))) {
  288: #if defined(HAVE_GDBM_H)
  289: 				PATCH(db);
  290: #endif
  291: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.trigger-timeout"))) {
  292: 				PATCH(trigger_timeout);
  293: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.debug"))) {
  294: 				PATCH(debug);
  295: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.deny-url"))) {
  296: 				PATCH(deny_url);
  297: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.memcache-namespace"))) {
  298: 				PATCH(mc_namespace);
  299: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.memcache-hosts"))) {
  300: #if defined(HAVE_MEMCACHE_H)
  301: 				PATCH(mc);
  302: #endif
  303: 			}
  304: 		}
  305: 	}
  306: 
  307: 	return 0;
  308: }
  309: #undef PATCH
  310: 
  311: URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) {
  312: 	plugin_data *p = p_d;
  313: 	const char *remote_ip;
  314: 	data_string *ds;
  315: 
  316: #if defined(HAVE_PCRE_H)
  317: 	int n;
  318: # define N 10
  319: 	int ovec[N * 3];
  320: 
  321: 	if (con->mode != DIRECT) return HANDLER_GO_ON;
  322: 
  323: 	if (con->uri.path->used == 0) return HANDLER_GO_ON;
  324: 
  325: 	mod_trigger_b4_dl_patch_connection(srv, con, p);
  326: 
  327: 	if (!p->conf.trigger_regex || !p->conf.download_regex) return HANDLER_GO_ON;
  328: 
  329: # if !defined(HAVE_GDBM_H) && !defined(HAVE_MEMCACHE_H)
  330: 	return HANDLER_GO_ON;
  331: # elif defined(HAVE_GDBM_H) && defined(HAVE_MEMCACHE_H)
  332: 	if (!p->conf.db && !p->conf.mc) return HANDLER_GO_ON;
  333: 	if (p->conf.db && p->conf.mc) {
  334: 		/* can't decide which one */
  335: 
  336: 		return HANDLER_GO_ON;
  337: 	}
  338: # elif defined(HAVE_GDBM_H)
  339: 	if (!p->conf.db) return HANDLER_GO_ON;
  340: # else
  341: 	if (!p->conf.mc) return HANDLER_GO_ON;
  342: # endif
  343: 
  344: 	if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "X-Forwarded-For"))) {
  345: 		/* X-Forwarded-For contains the ip behind the proxy */
  346: 
  347: 		remote_ip = ds->value->ptr;
  348: 
  349: 		/* memcache can't handle spaces */
  350: 	} else {
  351: 		remote_ip = inet_ntop_cache_get_ip(srv, &(con->dst_addr));
  352: 	}
  353: 
  354: 	if (p->conf.debug) {
  355: 		log_error_write(srv, __FILE__, __LINE__, "ss", "(debug) remote-ip:", remote_ip);
  356: 	}
  357: 
  358: 	/* check if URL is a trigger -> insert IP into DB */
  359: 	if ((n = pcre_exec(p->conf.trigger_regex, NULL, con->uri.path->ptr, con->uri.path->used - 1, 0, 0, ovec, 3 * N)) < 0) {
  360: 		if (n != PCRE_ERROR_NOMATCH) {
  361: 			log_error_write(srv, __FILE__, __LINE__, "sd",
  362: 					"execution error while matching:", n);
  363: 
  364: 			return HANDLER_ERROR;
  365: 		}
  366: 	} else {
  367: # if defined(HAVE_GDBM_H)
  368: 		if (p->conf.db) {
  369: 			/* the trigger matched */
  370: 			datum key, val;
  371: 
  372: 			key.dptr = (char *)remote_ip;
  373: 			key.dsize = strlen(remote_ip);
  374: 
  375: 			val.dptr = (char *)&(srv->cur_ts);
  376: 			val.dsize = sizeof(srv->cur_ts);
  377: 
  378: 			if (0 != gdbm_store(p->conf.db, key, val, GDBM_REPLACE)) {
  379: 				log_error_write(srv, __FILE__, __LINE__, "s",
  380: 						"insert failed");
  381: 			}
  382: 		}
  383: # endif
  384: # if defined(HAVE_MEMCACHE_H)
  385: 		if (p->conf.mc) {
  386: 			size_t i;
  387: 			buffer_copy_string_buffer(p->tmp_buf, p->conf.mc_namespace);
  388: 			buffer_append_string(p->tmp_buf, remote_ip);
  389: 
  390: 			for (i = 0; i < p->tmp_buf->used - 1; i++) {
  391: 				if (p->tmp_buf->ptr[i] == ' ') p->tmp_buf->ptr[i] = '-';
  392: 			}
  393: 
  394: 			if (p->conf.debug) {
  395: 				log_error_write(srv, __FILE__, __LINE__, "sb", "(debug) triggered IP:", p->tmp_buf);
  396: 			}
  397: 
  398: 			if (0 != mc_set(p->conf.mc,
  399: 					CONST_BUF_LEN(p->tmp_buf),
  400: 					(char *)&(srv->cur_ts), sizeof(srv->cur_ts),
  401: 					p->conf.trigger_timeout, 0)) {
  402: 				log_error_write(srv, __FILE__, __LINE__, "s",
  403: 						"insert failed");
  404: 			}
  405: 		}
  406: # endif
  407: 	}
  408: 
  409: 	/* check if URL is a download -> check IP in DB, update timestamp */
  410: 	if ((n = pcre_exec(p->conf.download_regex, NULL, con->uri.path->ptr, con->uri.path->used - 1, 0, 0, ovec, 3 * N)) < 0) {
  411: 		if (n != PCRE_ERROR_NOMATCH) {
  412: 			log_error_write(srv, __FILE__, __LINE__, "sd",
  413: 					"execution error while matching: ", n);
  414: 			return HANDLER_ERROR;
  415: 		}
  416: 	} else {
  417: 		/* the download uri matched */
  418: # if defined(HAVE_GDBM_H)
  419: 		if (p->conf.db) {
  420: 			datum key, val;
  421: 			time_t last_hit;
  422: 
  423: 			key.dptr = (char *)remote_ip;
  424: 			key.dsize = strlen(remote_ip);
  425: 
  426: 			val = gdbm_fetch(p->conf.db, key);
  427: 
  428: 			if (val.dptr == NULL) {
  429: 				/* not found, redirect */
  430: 
  431: 				response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url));
  432: 				con->http_status = 307;
  433: 				con->file_finished = 1;
  434: 
  435: 				return HANDLER_FINISHED;
  436: 			}
  437: 
  438: 			memcpy(&last_hit, val.dptr, sizeof(time_t));
  439: 
  440: 			free(val.dptr);
  441: 
  442: 			if (srv->cur_ts - last_hit > p->conf.trigger_timeout) {
  443: 				/* found, but timeout, redirect */
  444: 
  445: 				response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url));
  446: 				con->http_status = 307;
  447: 				con->file_finished = 1;
  448: 
  449: 				if (p->conf.db) {
  450: 					if (0 != gdbm_delete(p->conf.db, key)) {
  451: 						log_error_write(srv, __FILE__, __LINE__, "s",
  452: 								"delete failed");
  453: 					}
  454: 				}
  455: 
  456: 				return HANDLER_FINISHED;
  457: 			}
  458: 
  459: 			val.dptr = (char *)&(srv->cur_ts);
  460: 			val.dsize = sizeof(srv->cur_ts);
  461: 
  462: 			if (0 != gdbm_store(p->conf.db, key, val, GDBM_REPLACE)) {
  463: 				log_error_write(srv, __FILE__, __LINE__, "s",
  464: 						"insert failed");
  465: 			}
  466: 		}
  467: # endif
  468: 
  469: # if defined(HAVE_MEMCACHE_H)
  470: 		if (p->conf.mc) {
  471: 			void *r;
  472: 			size_t i;
  473: 
  474: 			buffer_copy_string_buffer(p->tmp_buf, p->conf.mc_namespace);
  475: 			buffer_append_string(p->tmp_buf, remote_ip);
  476: 
  477: 			for (i = 0; i < p->tmp_buf->used - 1; i++) {
  478: 				if (p->tmp_buf->ptr[i] == ' ') p->tmp_buf->ptr[i] = '-';
  479: 			}
  480: 
  481: 			if (p->conf.debug) {
  482: 				log_error_write(srv, __FILE__, __LINE__, "sb", "(debug) checking IP:", p->tmp_buf);
  483: 			}
  484: 
  485: 			/**
  486: 			 *
  487: 			 * memcached is do expiration for us, as long as we can fetch it every thing is ok
  488: 			 * and the timestamp is updated
  489: 			 *
  490: 			 */
  491: 			if (NULL == (r = mc_aget(p->conf.mc,
  492: 						 CONST_BUF_LEN(p->tmp_buf)
  493: 						 ))) {
  494: 
  495: 				response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url));
  496: 
  497: 				con->http_status = 307;
  498: 				con->file_finished = 1;
  499: 
  500: 				return HANDLER_FINISHED;
  501: 			}
  502: 
  503: 			free(r);
  504: 
  505: 			/* set a new timeout */
  506: 			if (0 != mc_set(p->conf.mc,
  507: 					CONST_BUF_LEN(p->tmp_buf),
  508: 					(char *)&(srv->cur_ts), sizeof(srv->cur_ts),
  509: 					p->conf.trigger_timeout, 0)) {
  510: 				log_error_write(srv, __FILE__, __LINE__, "s",
  511: 						"insert failed");
  512: 			}
  513: 		}
  514: # endif
  515: 	}
  516: 
  517: #else
  518: 	UNUSED(srv);
  519: 	UNUSED(con);
  520: 	UNUSED(p_d);
  521: #endif
  522: 
  523: 	return HANDLER_GO_ON;
  524: }
  525: 
  526: #if defined(HAVE_GDBM_H)
  527: TRIGGER_FUNC(mod_trigger_b4_dl_handle_trigger) {
  528: 	plugin_data *p = p_d;
  529: 	size_t i;
  530: 
  531: 	/* check DB each minute */
  532: 	if (srv->cur_ts % 60 != 0) return HANDLER_GO_ON;
  533: 
  534: 	/* cleanup */
  535: 	for (i = 0; i < srv->config_context->used; i++) {
  536: 		plugin_config *s = p->config_storage[i];
  537: 		datum key, val, okey;
  538: 
  539: 		if (!s->db) continue;
  540: 
  541: 		okey.dptr = NULL;
  542: 
  543: 		/* according to the manual this loop + delete does delete all entries on its way
  544: 		 *
  545: 		 * we don't care as the next round will remove them. We don't have to perfect here.
  546: 		 */
  547: 		for (key = gdbm_firstkey(s->db); key.dptr; key = gdbm_nextkey(s->db, okey)) {
  548: 			time_t last_hit;
  549: 			if (okey.dptr) {
  550: 				free(okey.dptr);
  551: 				okey.dptr = NULL;
  552: 			}
  553: 
  554: 			val = gdbm_fetch(s->db, key);
  555: 
  556: 			memcpy(&last_hit, val.dptr, sizeof(time_t));
  557: 
  558: 			free(val.dptr);
  559: 
  560: 			if (srv->cur_ts - last_hit > s->trigger_timeout) {
  561: 				gdbm_delete(s->db, key);
  562: 			}
  563: 
  564: 			okey = key;
  565: 		}
  566: 		if (okey.dptr) free(okey.dptr);
  567: 
  568: 		/* reorg once a day */
  569: 		if ((srv->cur_ts % (60 * 60 * 24) != 0)) gdbm_reorganize(s->db);
  570: 	}
  571: 	return HANDLER_GO_ON;
  572: }
  573: #endif
  574: 
  575: /* this function is called at dlopen() time and inits the callbacks */
  576: 
  577: int mod_trigger_b4_dl_plugin_init(plugin *p);
  578: int mod_trigger_b4_dl_plugin_init(plugin *p) {
  579: 	p->version     = LIGHTTPD_VERSION_ID;
  580: 	p->name        = buffer_init_string("trigger_b4_dl");
  581: 
  582: 	p->init        = mod_trigger_b4_dl_init;
  583: 	p->handle_uri_clean  = mod_trigger_b4_dl_uri_handler;
  584: 	p->set_defaults  = mod_trigger_b4_dl_set_defaults;
  585: #if defined(HAVE_GDBM_H)
  586: 	p->handle_trigger  = mod_trigger_b4_dl_handle_trigger;
  587: #endif
  588: 	p->cleanup     = mod_trigger_b4_dl_free;
  589: 
  590: 	p->data        = NULL;
  591: 
  592: 	return 0;
  593: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>