#include "httpd.h"      
#include "http_config.h"      
#include "http_request.h"      
#include "http_protocol.h"      
#include "http_core.h"      
#include "http_main.h"     
#include "http_log.h"      
   
/*     
 * C version of chapter 6's Apache::BlockAgent
 */     

/*
 * configure like so:
 *
 * LoadModule blockagent_module apxs/mod_blockagent.so
 * <BlockAgents>
 * ^teleport pro\/1\.28 
 * ^nicerspro 
 * ^mozilla\/3\.0 \(http engine\) 
 * ^netattache 
 * ^crescent internet toolpak http ole control v\.1\.0 
 * ^go-ahead-got-it 
 * ^wget 
 * ^devsoft's http component v1\.0 
 * ^www\.pl 
 * ^digout4uagent 
 * </BlockAgents>
 *
 */

module MODULE_VAR_EXPORT blockagent_module;

typedef struct {
    array_header *agents;
} blockagent_dir_config;

static void *blockagent_create_dir_config(pool *p, char *path)  
{  
    blockagent_dir_config *dcfg =   
        (blockagent_dir_config *)ap_pcalloc(p, sizeof(*dcfg));  
 
    dcfg->agents = ap_make_array(p, 10, sizeof(regex_t *));

    return (void *)dcfg;  
}  

static int blockagent_handler(request_rec *r)
{
    regex_t **agents;
    const char *ua = ap_table_get(r->headers_in, "User-Agent");
    int i;
    blockagent_dir_config *dcfg = 
	(blockagent_dir_config *)ap_get_module_config(r->per_dir_config, 
						      &blockagent_module);
    if (!ua) {
	return HTTP_FORBIDDEN;
    }

    agents = (regex_t **)dcfg->agents->elts;
    for (i=0; i<dcfg->agents->nelts; i++) {
        if (regexec(agents[i], ua, 0, NULL, 0) == 0) {    
	    ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,  
			  "Access forbidden to agent %s", ua);
	    return HTTP_FORBIDDEN;
        } 
    }

    return OK;
}

/* we dont re-read a custom configuration files as Apache::BlockAgent does
 * however, we do compile and cache the regexps to avoid request-time cost
 */
const char *blockagent_config_cmd(cmd_parms *parms, void *mconfig) 
{
    char line[MAX_STRING_LEN]; 
    blockagent_dir_config *dcfg = (blockagent_dir_config *)mconfig;
    int re_flags = REG_EXTENDED|REG_ICASE|REG_NOSUB; 

    while (!ap_cfg_getline(line, sizeof(line), parms->config_file)) { 
	/* stop if we hit the container end token */
	if (strcasecmp(line, "</BlockAgents>") == 0) { 
	    break; 
	} 
	/* skip blank lines and comments */
	if (!*line || *line == '#') {
	    continue;
	}
	/* compile the regex */
	*(regex_t **)ap_push_array(dcfg->agents) = 
	    ap_pregcomp(parms->pool, line, re_flags);
    } 

    return NULL;
}

static const char *blockagent_config_cmd_end(cmd_parms *parms, void *mconfig) { 
    return ap_pstrcat(parms->pool, parms->cmd->name, 
		      " without matching <", 
		      parms->cmd->name + 2, " section", NULL); 
} 

static command_rec blockagent_cmds[] = { 
    {"<BlockAgents>", blockagent_config_cmd, NULL, 
     OR_ALL, NO_ARGS, "section of agent patterns"}, 
    {"</BlockAgents>", blockagent_config_cmd_end, NULL,  
     OR_ALL, NO_ARGS, "end of blockagents"},  
    {NULL},
};

module MODULE_VAR_EXPORT blockagent_module = {
    STANDARD_MODULE_STUFF, 
    NULL,                  /* module initializer                  */
    blockagent_create_dir_config, /* create per-dir    config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    blockagent_cmds,       /* table of config file commands       */
    NULL,                  /* [#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_ */
    blockagent_handler,    /* [#3] check access by host address   */
    NULL,                  /* [#6] determine MIME type            */
    NULL,                  /* [#7] pre-run fixups                 */
    NULL,                  /* [#9] log a transaction              */
    NULL,                  /* [#2] header parser                  */
    NULL,                  /* child_init                          */
    NULL,                  /* child_exit                          */
    NULL                   /* [#0] post read-request              */
};

