/* ** mod_trigger.c -- Fire off triggers for events ** -Brian (brian@tangent.org) */ #include "httpd.h" #include "http_config.h" #include "http_protocol.h" #include "ap_config.h" #include "fnmatch.h" #include "http_log.h" /* Strings for help */ #define TriggerEngine "On or Off (Off by default). This enables the trigger engine." #define TriggerLog "Triggers will send a log message to the error log when found." #define TriggerHandler "Supply a handler and then either a script or uri to call if the handler is found." #define TriggerURI "Supply a URI and then either a script or uri to call if the URI is found." #define TriggerAgent "Supply a browser agent and then either a script or uri to call if the agent is found." #define TriggerReferer "Supply a referer and then either a script or uri to call if the referer is found." #define TriggerMime "Supply a mime-type and then either a script or uri to call if the mime-type is found." #define TriggerAddress "Supply an IP address and then either a script or uri to call if the address is found." #define TriggerUser "Supply a username (REMOTE_USER) and then either a script or uri to call if the user is found." #define TriggerIdent "Supply an ident and then either a script or uri to call if the ident is found." #define TriggerPathInfo "Supply a pthinfo and then either a script or uri to call if the pathinfo is found." #define TriggerAccept "Supply an accept header and then either a script or uri to call if the accept header is found." #define TriggerCookie "Supply a cookie name and then either a script or uri to call if the cookie is found." #define WATCHPOINT printf("WATCHPOINT %s %d\n", __FILE__, __LINE__); module MODULE_VAR_EXPORT trigger_module; typedef struct { int enabled; int log; table *handler; table *uri; table *agent; table *referer; table *mime; table *address; table *user; table *ident; table *pathinfo; table *accept; table *cookie; table *args; } trigger_conf; static void *create_dir_mconfig(pool *p, char *dir) { trigger_conf *cfg; cfg = ap_pcalloc(p, sizeof(trigger_conf)); cfg->enabled=0; cfg->log=0; cfg->handler = NULL; cfg->uri = NULL; cfg->agent = NULL; cfg->referer = NULL; cfg->mime = NULL; cfg->address = NULL; cfg->user = NULL; cfg->ident = NULL; cfg->pathinfo = NULL; cfg->accept = NULL; cfg->cookie = NULL; cfg->args = NULL; return (void *) cfg; } int call_container(request_rec *r, char *uri) { int status = OK; request_rec *subr; subr = (request_rec *) ap_sub_req_lookup_uri(uri, r); ap_table_setn(subr->headers_in, "Content-Length", "0"); subr->assbackwards = 1; ap_table_setn(subr->subprocess_env, "TRIGGER_SCRIPT_NAME", r->uri); ap_table_setn(subr->subprocess_env, "TRIGGER_PATH_INFO", r->path_info); ap_table_setn(subr->subprocess_env, "TRIGGER_QUERY_STRING", r->args); ap_table_setn(subr->subprocess_env, "TRIGGER_FILENAME", r->filename); subr->args = r->args; status = ap_run_sub_req(subr); ap_destroy_sub_req(subr); return status; } static int call_program(void *rp, child_info *pinfo) { char **env; request_rec *r = (request_rec *)rp; ap_add_cgi_vars(r); env = (char **)ap_create_environment(r->pool, r->subprocess_env); ap_error_log2stderr(r->server); ap_cleanup_for_exec(); (void)ap_call_exec(r, pinfo, r->filename, env, 0); ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "exec for %s failed", r->filename); exit(0); return 0; } static void execute(request_rec *r, char *string) { int status = OK; struct stat sbuf; char *temp = NULL; BUFF *pipe; /* Ok, reality? We should set this up to determine this ahead of time. */ ap_table_setn(r->subprocess_env, "TRIGGER_SCRIPT_NAME", r->uri); ap_table_setn(r->subprocess_env, "TRIGGER_PATH_INFO", r->path_info); ap_table_setn(r->subprocess_env, "TRIGGER_QUERY_STRING", r->args); ap_table_setn(r->subprocess_env, "TRIGGER_FILENAME", r->filename); if(!stat(string, &sbuf)) { temp = r->filename; r->filename = string; if(!ap_bspawn_child(r->pool, call_program, (void *) r, kill_after_timeout, NULL, &pipe, NULL)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "could not spawn: %s", string); } r->filename = temp; } else { if ((status = call_container(r, string)) != OK) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "The following error occured while processing the Triger : %s : %d", string, status); } } } void table_execute(request_rec *r, const table *t, const char *key, int log) { array_header *hdrs_arr; table_entry *elts; int x = 0; if (key == NULL) return; if (t == NULL) return; hdrs_arr = ap_table_elts(t); elts = (table_entry *) hdrs_arr->elts; for (x = 0; x < hdrs_arr->nelts; ++x) { if (!ap_fnmatch(elts[x].key, key, FNM_CASE_BLIND)) { execute(r, elts[x].val); if(log) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, "mod_trigger:Firing off trigger %s, for %s", elts[x].val, elts[x].key); } } return; } /* Borrowed from mod_rewrite (and modified) */ static char *lookup_header(request_rec *r, const char *name) { array_header *hdrs_arr; table_entry *hdrs; int i; hdrs_arr = ap_table_elts(r->headers_in); hdrs = (table_entry *)hdrs_arr->elts; for (i = 0; i < hdrs_arr->nelts; ++i) { if (hdrs[i].key == NULL) { continue; } if (!strcasecmp(hdrs[i].key, name)) { return hdrs[i].val; } } return NULL; } void table_print(request_rec *r, table *t, char *caption) { array_header *hdrs_arr; table_entry *elts; int x = 0; if (t == NULL) return; hdrs_arr = ap_table_elts(t); elts = (table_entry *) hdrs_arr->elts; ap_rprintf(r, "Caption: %s\n", caption); for (x = 0; x < hdrs_arr->nelts; ++x) { ap_rprintf(r, "%s:%s\n", elts[x].key, elts[x].val); } } static int trigger_handler(request_rec *r) { trigger_conf *cfg; cfg = ap_get_module_config(r->per_dir_config, &trigger_module); r->content_type = "text/html"; ap_rputs(DOCTYPE_HTML_3_2 "\nApache Status\n\n", r); ap_rputs("

Apache Server Status for ", r); ap_rvputs(r, ap_get_server_name(r), "

\n\n", NULL); ap_rvputs(r, "Server Version: ", ap_get_server_version(), "
\n", NULL); ap_rvputs(r, "Server Built: ", ap_get_server_built(), "
\n
\n", NULL); /* ap_rvputs(r, "Current Time: ", ap_ht_time(r->pool, nowtime, DEFAULT_TIME_FORMAT, 0), "
\n", NULL); ap_rvputs(r, "Restart Time: ", ap_ht_time(r->pool, ap_restart_time, DEFAULT_TIME_FORMAT, 0), "
\n", NULL); */ ap_send_http_header(r); if (r->header_only) return OK; if(cfg->handler) table_print(r, cfg->handler, "This is the default caption" ); if(cfg->uri) table_print(r, cfg->uri, "This is the default caption" ); if(cfg->agent) table_print(r, cfg->agent, "This is the default caption" ); if(cfg->referer) table_print(r, cfg->referer, "This is the default caption" ); if(cfg->mime) table_print(r, cfg->mime, "This is the default caption" ); if(cfg->address) table_print(r, cfg->address, "This is the default caption" ); if(cfg->user) table_print(r, cfg->user, "This is the default caption" ); if(cfg->ident) table_print(r, cfg->ident, "This is the default caption" ); if(cfg->pathinfo) table_print(r, cfg->pathinfo,"This is the default caption" ); if(cfg->accept) table_print(r, cfg->accept, "This is the default caption" ); if(cfg->cookie) table_print(r, cfg->cookie, "This is the default caption" ); if(cfg->args) table_print(r, cfg->args, "This is the default caption" ); ap_rputs("", r); return OK; } static int trigger_log (request_rec *r) { trigger_conf *cfg; cfg = ap_get_module_config(r->per_dir_config, &trigger_module); if(!cfg->enabled) return DECLINED; if(cfg->handler) table_execute(r, cfg->handler, r->handler, cfg->log); if(cfg->uri) table_execute(r, cfg->uri, r->uri, cfg->log); if(cfg->agent) table_execute(r, cfg->agent, lookup_header(r, "User-Agent"), cfg->log); if(cfg->referer) table_execute(r, cfg->referer, lookup_header(r, "Referer"), cfg->log); if(cfg->mime) table_execute(r, cfg->mime, r->content_type, cfg->log); if(cfg->address) table_execute(r, cfg->address, r->connection->remote_ip, cfg->log); if(cfg->user) table_execute(r, cfg->user, r->connection->user, cfg->log); if(cfg->ident) table_execute(r, cfg->ident, (char *)ap_get_remote_logname(r, r), cfg->log); if(cfg->pathinfo) table_execute(r, cfg->pathinfo, r->path_info, cfg->log); if(cfg->accept) table_execute(r, cfg->accept, lookup_header(r, "Accept"), cfg->log); if(cfg->cookie) table_execute(r, cfg->cookie, lookup_header(r, "Cookie"), cfg->log); if(cfg->args) table_execute(r, cfg->args, r->args, cfg->log); return DECLINED; } /* Dispatch list of content handlers */ static const handler_rec trigger_handlers[] = { { "trigger-status", trigger_handler }, { NULL, NULL } }; static const char * add_trigger (cmd_parms *cmd, void *mconfig, char *key, char *value) { trigger_conf *cfg = (trigger_conf *) mconfig; if(!strcasecmp(cmd->cmd->name, "TriggerHandler")) { if(!cfg->handler) cfg->handler = ap_make_table(cmd->pool, 1); ap_table_set(cfg->handler, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerURI")) { if(!cfg->uri) cfg->uri = ap_make_table(cmd->pool, 1); ap_table_set(cfg->uri, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerAgent")) { if(!cfg->agent) cfg->agent = ap_make_table(cmd->pool, 1); ap_table_set(cfg->agent, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerReferer")) { if(!cfg->referer) cfg->referer = ap_make_table(cmd->pool, 1); ap_table_set(cfg->referer, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerMime")) { if(!cfg->mime) cfg->mime = ap_make_table(cmd->pool, 1); ap_table_set(cfg->mime, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerAddress")) { if(!cfg->address) cfg->address = ap_make_table(cmd->pool, 1); ap_table_set(cfg->address, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerUser")) { if(!cfg->user) cfg->user = ap_make_table(cmd->pool, 1); ap_table_set(cfg->user, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerIdent")) { if(!cfg->ident) cfg->ident = ap_make_table(cmd->pool, 1); ap_table_set(cfg->ident, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerPathInfo")) { if(!cfg->pathinfo) cfg->pathinfo = ap_make_table(cmd->pool, 1); ap_table_set(cfg->pathinfo, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerAccept")) { if(!cfg->accept) cfg->accept = ap_make_table(cmd->pool, 1); ap_table_set(cfg->accept, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerCookie")) { if(!cfg->cookie) cfg->cookie = ap_make_table(cmd->pool, 1); ap_table_set(cfg->cookie, key, value); } else if(!strcasecmp(cmd->cmd->name, "TriggerArgs")) { if(!cfg->args) cfg->args = ap_make_table(cmd->pool, 1); ap_table_set(cfg->args, key, value); } return NULL; } static const command_rec trigger_cmds[] = { {"TriggerEngine", ap_set_flag_slot, (void *) XtOffsetOf(trigger_conf, enabled), OR_ALL, TAKE1, TriggerHandler}, {"TriggerLog", ap_set_flag_slot, (void *) XtOffsetOf(trigger_conf, log), OR_ALL, TAKE1, TriggerLog}, {"TriggerHandler", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerURI", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerAgent", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerReferer", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerMime", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerAddress", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerUser", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerIdent", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerPathInfo", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerAccept", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {"TriggerCookie", add_trigger, NULL, OR_ALL, TAKE2, TriggerHandler}, {NULL}, }; /* Dispatch list for API hooks */ module MODULE_VAR_EXPORT trigger_module = { STANDARD_MODULE_STUFF, NULL, /* module initializer */ create_dir_mconfig, /* create per-dir config structures */ NULL, /* merge per-dir config structures */ NULL, /* create per-server config structures */ NULL, /* merge per-server config structures */ trigger_cmds, /* table of config file commands */ trigger_handlers, /* [#8] MIME-typed-dispatched handlers */ NULL, /* [#1] URI to filename translation */ NULL, /* [#4] validate user id from request */ NULL, /* [#5] check if the user is ok _here_ */ NULL, /* [#3] check access by host address */ NULL, /* [#6] determine MIME type */ NULL, /* [#7] pre-run fixups */ trigger_log, /* [#9] log a transaction */ NULL, /* [#2] header parser */ NULL, /* child_init */ NULL, /* child_exit */ NULL /* [#0] post read-request */ };