/* 
**  mod_layout.c -- Apache layout module
**  $Revision: 1.10 $
*/ 

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include "http_log.h"


typedef struct {
	int enabled;
	int proxy;
  int headertype;
  int footertype;
  char *header;
  char *footer;
  table *types;
} layout_conf;

module MODULE_VAR_EXPORT layout_module;

static void *create_dir_mconfig(pool *p, char *dir) {
	layout_conf *cfg;
	cfg = ap_pcalloc(p, sizeof(layout_conf));
	cfg->enabled = 0;
	cfg->proxy = 0;
	cfg->types = ap_make_table(p, 4);

	ap_table_set(cfg->types, INCLUDES_MAGIC_TYPE, "enabled");
	ap_table_set(cfg->types, INCLUDES_MAGIC_TYPE3, "enabled");
	ap_table_set(cfg->types, "server-parsed", "enabled");
	ap_table_set(cfg->types, "text/html", "enabled");
	ap_table_set(cfg->types, "text/plain", "enabled");
	ap_table_set(cfg->types, "perl-script", "enabled");
	ap_table_set(cfg->types, "cgi-script", "enabled");
	ap_table_set(cfg->types, "application/x-httpd-cgi", "enabled");

	return (void *)cfg;
}


static int include_virtual(request_rec *r, char *uri, char *method) {
	int status = OK;
	request_rec *subr;
	subr = (request_rec *) ap_sub_req_method_uri(method, uri, r);
	status = ap_run_sub_req(subr);
	ap_destroy_sub_req(subr);

	return status;
}

static int include_virtual_container(request_rec *r, char *uri) {
	int status = OK;
	request_rec *subr;
	subr = (request_rec *) ap_sub_req_method_uri("GET", uri, r);
	ap_table_set(subr->subprocess_env, "LAYOUT_SCRIPT_NAME", r->uri);
	ap_table_set(subr->subprocess_env, "LAYOUT_PATH_INFO", r->path_info);
	ap_table_set(subr->subprocess_env, "LAYOUT_QUERY_STRING", r->args);
	status = ap_run_sub_req(subr);
	ap_destroy_sub_req(subr);

	return status;
}

static int layout_handler(request_rec *r) {
	int status;
	request_rec *subr;
	layout_conf *cfg = ap_get_module_config(r->per_dir_config, &layout_module);
	const char *content_length;
	if (r->main) {
		return DECLINED;
	}

	if ((r->finfo.st_mode == 0) && (r->proxyreq == 0)) {
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "File not found: %s", (r->path_info ? ap_pstrcat(r->pool, r->filename, r->path_info, NULL) : r->filename));
		return HTTP_NOT_FOUND;
	}

	r->content_type = "text/html";
	ap_update_mtime(r, r->finfo.st_mtime);
	ap_set_last_modified(r);
/*	ap_set_etag(r); */
	ap_send_http_header(r);

	if (r->header_only) {
		return OK;
	}
/* 
	 So you are asking, what is up with Content-Length? Well to make CGI's
	 work we have to spoof it a bit. Namely, if Content-Length is set when
	 mod_cgi runs, mod_cgi will try to read the request. Now if your CGI
	 gets it contents through a POST method this of course is a no go since
	 all of the contents will have already been read (and Apache will deadlock
	 trying to read from a stream with no data in it. To get around this we
	 spoof the content length till the original request runs 
*/
	content_length = ap_pstrdup(r->pool, ap_table_get(r->headers_in, "Content-Length"));
	if (cfg->header) {
		if(cfg->headertype) {
			ap_rputs(cfg->header, r);
		} else {
			ap_table_set(r->headers_in, "Content-Length", "0");
			(void)include_virtual_container(r,cfg->header);
		}
	}

	/* 
		 Now we handle the orignal request.
	 */
	ap_table_set(r->headers_in, "Content-Length", content_length);
	(void)include_virtual(r,r->unparsed_uri,(char *)r->method); 

	if (cfg->footer) {
		if(cfg->footertype) {
			ap_rputs(cfg->footer, r);
		} else {
			/* Just to be paranoid */
			ap_table_set(r->headers_in, "Content-Length", "0");
			(void)include_virtual_container(r,cfg->footer);
		}
	}

	return OK;
}

static int layout_fixup(request_rec *r) {
	layout_conf *cfg = ap_get_module_config(r->per_dir_config, &layout_module);
	request_rec *subr;
	const char *type;

	if (!cfg->enabled) {
		return DECLINED;
	}
	if (r->main) {
		return DECLINED;
	}

	if(cfg->proxy && r->proxyreq) {
	/* 
		 Damn! Ok, here is the problem. If the request is for something
		 which is and index how do we determine its mime type? Currently
		 we just assume that it is a NULL and wrap it. This is far
		 from perfect and is still pretty much a shot in the dark.
		 More research needed.
  */
		subr = (request_rec *) ap_sub_req_lookup_file(r->uri, r);
		type = subr->content_type;
		ap_destroy_sub_req(subr);

		if (ap_table_get(cfg->types, r->content_type) || r->content_type == NULL) { 
			r->handler = "layout";
			return DECLINED;
		}
	}
	if (!ap_table_get(cfg->types, r->content_type) ) { 
			return DECLINED;
	}
	r->handler = "layout";

	return DECLINED;
}

/* Dispatch list of content handlers */
static const handler_rec layout_handlers[] = { 
    { "layout", layout_handler }, 
    { NULL }
};

static const char *
add_header (cmd_parms *cmd, void *mconfig, char *uri) {
  layout_conf *cfg = (layout_conf *) mconfig;

  cfg->header = ap_pstrdup(cmd->pool, uri);
  cfg->enabled = 1;

  return NULL;
}

static const char *
add_footer (cmd_parms *cmd, void *mconfig, char *uri) {
  layout_conf *cfg;
	char *trash;
  cfg = (layout_conf *) mconfig;

  cfg->enabled = 1;
  cfg->footer = ap_pstrdup(cmd->pool, uri);

  return NULL;
}

static const char *
add_header_txt (cmd_parms *cmd, void *mconfig, char *uri) {
  layout_conf *cfg = (layout_conf *) mconfig;

  cfg->header = ap_pstrdup(cmd->pool, uri);
  cfg->headertype = 1;
  cfg->enabled = 1;

  return NULL;
}

static const char *
add_footer_txt (cmd_parms *cmd, void *mconfig, char *uri) {
  layout_conf *cfg;
  cfg = (layout_conf *) mconfig;

  cfg->footer = ap_pstrdup(cmd->pool, uri);
  cfg->footertype = 1;
  cfg->enabled = 1;

  return NULL;
}

static const char *
add_type (cmd_parms *cmd, void *mconfig, char *type) {
  layout_conf *cfg;
  cfg = (layout_conf *) mconfig;
	ap_table_addn(cfg->types, type, "enabled");

  return NULL;
}

static const char *
layout_proxy (cmd_parms *cmd, void *mconfig, int flag) {
  layout_conf *cfg;
  cfg = (layout_conf *) mconfig;
	cfg->proxy = flag;

  return NULL;
}

static const char *
remove_default_types (cmd_parms *cmd, void *mconfig, int flag) {
  layout_conf *cfg;
	if (flag) {
		return NULL;
	}
  cfg = (layout_conf *) mconfig;
	ap_table_unset(cfg->types, INCLUDES_MAGIC_TYPE);
	ap_table_unset(cfg->types, INCLUDES_MAGIC_TYPE3);
	ap_table_unset(cfg->types, "server-parsed");
	ap_table_unset(cfg->types, "text/html");
	ap_table_unset(cfg->types, "text/plain");
	ap_table_unset(cfg->types, "perl-script");
	ap_table_unset(cfg->types, "cgi-script");
	ap_table_unset(cfg->types, "application/x-httpd-cgi");

  return NULL;
}

static const command_rec layout_cmds[] =
{
	{ "Header", add_header, NULL, OR_ALL, TAKE1, "A filename with the footer contents." },
	{ "HeaderTXT", add_header_txt, NULL, OR_ALL, TAKE1, "Double Quoted text" },
	{ "Footer", add_footer, NULL, OR_ALL, TAKE1, "A filename with the footer contents." },
	{ "FooterTXT", add_footer_txt, NULL, OR_ALL, TAKE1, "Double Quoted text" },
	{ "LayoutHandler", add_type, NULL, OR_ALL, TAKE1, "Enter either a mime type or a handler type." },
	{ "LayoutProxy", layout_proxy, NULL, OR_ALL, FLAG, "This can either be On or Off (default it Off)." },
	{ "LayoutDefaultHandlers", remove_default_types, NULL, OR_ALL, FLAG, "Turns On (default) or Off a list of standard types to handle." },
			  { NULL },
};

static void layout_init(server_rec *s, pool *p) {
	  /* Tell apache we're here */
	  ap_add_version_component("mod_layout/1.0");
}

/* Dispatch list for API hooks */
module MODULE_VAR_EXPORT layout_module = {
    STANDARD_MODULE_STUFF, 
    layout_init,                  /* 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 */
    layout_cmds,           /* table of config file commands       */
    layout_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            */
    layout_fixup,                /* [#7] pre-run fixups                 */
    NULL,                  /* [#9] log a transaction              */
    NULL,                  /* [#2] header parser                  */
    NULL,                  /* child_init                          */
    NULL,                  /* child_exit                          */
    NULL                   /* [#0] post read-request              */
};

