#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 4's Apache::NavBar
 */

/*
 * configure like so:
 * <Location />
 *   <NavBar>
 *      /index.html    Home 
 *      /products/     Products 
 *      /download/     Download 
 *      /support/      Tech Support 
 *      /contact/      Contact Us 
 *   </NavBar>
 *   SetHandler navbar
 * </Location>
 */

module MODULE_VAR_EXPORT navbar_module;

/* per-directory configuration */
typedef struct {
    table *labels;
    char *table_attr;
    char *table_bg_color;
    char *table_active_color;
    time_t mtime;
} navbar_dir_config;

/* per-server configuration */
typedef struct {
    char *navbar_tok;
    int navbar_tok_len;
} navbar_srv_config;

/* per-request configuration (cache in our case) */
typedef struct {
    char *navbar;
    int navbar_len;
} navbar_req_config;

static void *navbar_create_dir_config(pool *p, char *path) 
{ 
    navbar_dir_config *dcfg =  
        (navbar_dir_config *)ap_pcalloc(p, sizeof(*dcfg)); 

    dcfg->labels             = ap_make_table(p, 10);
    dcfg->table_attr         = "WIDTH=\"100%\" BORDER=1";
    dcfg->table_bg_color     = "#C8FFFF";
    dcfg->table_active_color = "#FF0000";
    dcfg->mtime = 0;

    return (void *)dcfg; 
} 

static void *navbar_create_srv_config(pool *p, server_rec *s) 
{
    navbar_srv_config *scfg = 
       (navbar_srv_config *)ap_pcalloc(p, sizeof(*scfg)); 

    scfg->navbar_tok = "<!--NAVBAR-->";
    scfg->navbar_tok_len = strlen(scfg->navbar_tok);

    return (void *)scfg; 
}

static void *navbar_merge_dir_config(pool *p, void *base, void *new) 
{
    navbar_dir_config *merged = 
	(navbar_dir_config *)ap_pcalloc(p, sizeof(*merged)); 
    navbar_dir_config *parent = (navbar_dir_config *)base; 
    navbar_dir_config *child  = (navbar_dir_config *)new; 
 
    merged->table_attr = child->table_attr ?  
	child->table_attr : parent->table_attr; 

    merged->table_bg_color = child->table_bg_color ?  
	child->table_bg_color : parent->table_bg_color; 

    merged->table_active_color = child->table_active_color ?  
	child->table_active_color : parent->table_active_color; 

    merged->table_active_color = child->table_active_color ?  
	child->table_active_color : parent->table_active_color; 

    merged->labels = ap_overlay_tables(p, parent->labels, child->labels); 

    merged->mtime = child->mtime ? child->mtime : parent->mtime;

    return (void*)merged; 
}

const char *navbar_config_cmd(cmd_parms *parms, void *mconfig, const char *arg) 
{
    char line[MAX_STRING_LEN]; 
    char *endp = NULL;
    navbar_dir_config *dcfg = (navbar_dir_config *)mconfig;

    if (parms->cmd->args_how != NO_ARGS && arg) {
	if (!(endp = strrchr(arg, '>'))) { 
	    return "no terminal \">\" sign"; 
	} 
	*endp = '\0'; 
	dcfg->table_attr = ap_pstrdup(parms->pool, arg);
    }

    while (!ap_cfg_getline(line, sizeof(line), parms->config_file)) { 
	const char *lp = line;
	const char *url;

	/* stop if we hit the container end token */
	if (!strcasecmp(line, "</NavBar>")) { 
	    break; 
	} 
	/* skip blank lines and comments */
	if (!*lp || *lp == '#') {
	    continue;
	}
	/* split on whitespace into url => label pair */
	url = ap_getword_white(parms->temp_pool, &lp);
	ap_table_set(dcfg->labels, url, lp);
    } 

    /* note configuration file last modified timestamp */
    if (!dcfg->mtime) {
	struct stat fi;
	stat(parms->config_file->name, &fi);
	dcfg->mtime = fi.st_mtime;
    }

    return NULL;
}

const char *navbar_config_cmd_no_args(cmd_parms *parms, void *mconfig) 
{
    return navbar_config_cmd(parms, mconfig, NULL);
}

static const char *navbar_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 navbar_req_config *make_bar(request_rec *r)
{
    navbar_req_config *rcfg = (navbar_req_config *)
	ap_get_module_config(r->request_config, &navbar_module);
    navbar_dir_config *dcfg = (navbar_dir_config *)
	ap_get_module_config(r->per_dir_config, &navbar_module);

    int i;
    char *table_head;
    array_header *arr, *cells;
    table_entry *elts;

    /* only generate the navbar once per-request */
    if (rcfg) {
	return rcfg;
    }
    else {
	rcfg = (navbar_req_config *)ap_pcalloc(r->pool, sizeof(*rcfg));
    }
    
    table_head = ap_psprintf(r->pool, 
			     "<TABLE %s><TR>", dcfg->table_attr);
    arr = ap_table_elts(dcfg->labels);
    elts = (table_entry *)arr->elts; 
    cells = ap_make_array(r->pool, arr->nelts, sizeof(char *));

    for (i = 0; i < arr->nelts; ++i) { 
	char *cell;
	char *uri = elts[i].key, *label = elts[i].val;

        if (!uri || !label) {
	    continue;
	}

	cell = strncmp(r->main ? r->main->uri : r->uri, uri, strlen(uri)) ?
	    ap_psprintf(r->pool, "<A HREF=\"%s\">%s</A>", uri, label) :
	    ap_psprintf(r->pool, "<FONT COLOR=\"%s\">%s</FONT>",
			dcfg->table_active_color, label);

	*(char **)ap_push_array(cells) = 
	    ap_psprintf(r->pool, 
			"<TD CLASS=\"navbar\" ALIGN=CENTER BGCOLOR=\"%s\">%s</TD>\n", 
			dcfg->table_bg_color, cell);
    } 

    rcfg->navbar = ap_pstrcat(r->pool, table_head, 
			      ap_array_pstrcat(r->pool, cells, ' '),
			      "</TR></TABLE>\n", NULL);
    rcfg->navbar_len = strlen(rcfg->navbar);
    ap_set_module_config(r->request_config, &navbar_module, rcfg);

    return rcfg;
}

static void navbar_insert(request_rec *r, char *chunk, long len)
{
    navbar_srv_config *scfg = (navbar_srv_config *)
	ap_get_module_config(r->server->module_config, &navbar_module);
    char *ptr = chunk;
    char *sub;

    while ((sub = strstr(ptr, scfg->navbar_tok))) {
	navbar_req_config *rcfg = make_bar(r);
	int end = sub - ptr;
	ap_rwrite(ptr, end, r);
	ap_rwrite(rcfg->navbar, rcfg->navbar_len, r);
	ptr += end + scfg->navbar_tok_len;
    }

    ap_rputs(ptr, r);
}

static void navbar_send(request_rec *r, FILE *f, long length)
{
    char buf[IOBUFSIZE];
    int n;

    ap_soft_timeout("navbar send", r);
    while ((n = fread(buf, sizeof(char), sizeof(buf), f)) > 0) {
	buf[n] = '\0';
	navbar_insert(r, buf, n);
	ap_reset_timeout(r);   /* reset timeout after successful write */
    }
    ap_kill_timeout(r);
}

static int navbar_handler(request_rec *r)
{
    navbar_dir_config *dcfg = (navbar_dir_config *)
	ap_get_module_config(r->per_dir_config, &navbar_module);
    int rc;
    FILE *f;

    if (r->method_number != M_GET) {
	return DECLINED;
    }

    if (r->content_type && strcasecmp(r->content_type, "text/html") != 0) {
	return DECLINED;
    }

    if (!(f = ap_pfopen(r->pool, r->filename, "r"))) {
        return DECLINED;
    }

    /* 
     * either config file or requested file modification should
     * invalidate the client cache
     */
    ap_update_mtime(r, dcfg->mtime > r->finfo.st_mtime ? 
		    dcfg->mtime : r->finfo.st_mtime);
    ap_set_last_modified(r);
    ap_set_etag(r);

    if ((rc = ap_meets_conditions(r)) != OK) {
        return rc;
    }

    ap_send_http_header(r);

    if (r->header_only) {
	return OK;
    }

    navbar_send(r, f, r->finfo.st_size);

    return OK;
}

static command_rec navbar_cmds[] = { 
    /* for including table attributes */
    {"<NavBar", navbar_config_cmd, NULL, 
     OR_ALL, RAW_ARGS, "a navbar"}, 
    /* no table attributes */
    {"<NavBar>", navbar_config_cmd_no_args, NULL, 
     OR_ALL, NO_ARGS, "a navbar"}, 
    {"</NavBar>", navbar_config_cmd_end, NULL, 
     OR_ALL, NO_ARGS, "end of navbar"}, 
    { 
	"NavBarBackGroundColor", 
	ap_set_string_slot, 
	(void *)XtOffsetOf(navbar_dir_config, table_bg_color), 
	OR_ALL, 
	TAKE1, 
	"NavBar table background color",
    }, 
    { 
	"NavBarActiveColor", 
	ap_set_string_slot, 
	(void *)XtOffsetOf(navbar_dir_config, table_active_color), 
	OR_ALL, 
	TAKE1, 
	"NavBar active color",
    }, 

    { NULL },
};

static const handler_rec navbar_handlers[] = { 
    { "navbar", navbar_handler }, 
    { NULL, NULL }
};

module MODULE_VAR_EXPORT navbar_module = {
    STANDARD_MODULE_STUFF, 
    NULL,                  /* module initializer                  */
    navbar_create_dir_config, /* create per-dir    config structures */
    navbar_merge_dir_config,  /* merge  per-dir    config structures */
    navbar_create_srv_config, /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    navbar_cmds,           /* table of config file commands       */
    navbar_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                 */
    NULL,                  /* [#9] log a transaction              */
    NULL,                  /* [#2] header parser                  */
    NULL,                  /* child_init                          */
    NULL,                  /* child_exit                          */
    NULL                   /* [#0] post read-request              */
};

