/*
** 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 */
};