/*
** mod_html2hdml.c -- Apache html2hdml module
*/
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include "http_log.h"
//#include "alloc.h"
#include "fnmatch.h"
#include
#include
#include
#include
#include "strinput.h"
#include "html2hdml.h"
#include "mod_html2hdml.h"
#ifdef ZODIAX_WITH_HTML2HDML
#include "module.h"
#endif
#include "config.h"
#define UNSET (-1)
#define OFF (0)
#define ON (1)
#define WATCHPOINT printf("WATCHPOINT %s %d\n", __FILE__, __LINE__);
module MODULE_VAR_EXPORT html2hdml_module;
struct pool *gl_pool;
int set_opt_for_apmodule(void)
{
gl_clientinfo.row = 10;
gl_clientinfo.column = 10;
gl_convertopt.a_href_html2hdml = 0;
gl_convertopt.img = 1;
gl_convertopt.img_src_gif2bmp = 1;
gl_convertopt.img_alt = 0;
return 0;
}
#define MOD_HTML2HDML_DEBUG
int html2hdml_convert(FILE *fp, request_rec *r)
{
char buf_stack[BUFFER_SIZE], *buf, *ptr;
int n;
int need_to_close = 1;
int heap_used = 0;
int bufsize;
#ifdef MOD_HTML2HDML_DEBUG /* must refine */
int header_size = 0;
#endif
set_opt_for_apmodule();
#ifdef MOD_HTML2HDML
gl_pool = r->pool;
gl_convertopt.apache_r = r;
#endif
if(fp == NULL) return 0;
n = fread(buf_stack, sizeof(char), BUFFER_SIZE-1, fp);
buf_stack[n] = '\0';
if (feof(fp)) {
buf = buf_stack;
bufsize = n+1;
} else {
heap_used = 1;
bufsize = n+1;
buf = malloc(bufsize*sizeof(char));
ptr = buf;
/* realloc & copy */
memcpy(ptr, buf_stack, n);
do {
n = fread(buf_stack, sizeof(char), BUFFER_SIZE-1, fp);
buf_stack[n] = '\0';
if (n) {
buf = realloc(buf, (bufsize+n)*sizeof(char));
ptr = buf+bufsize-1;
bufsize += n;
memcpy(ptr, buf_stack, n);
}
} while (!feof(fp));
buf[bufsize-1] = '\0';
}
//if (need_to_close) fclose(fp);
#ifdef MOD_HTML2HDML_DEBUG /* must refine */
{
char *tmp_p;
tmp_p = strstr(buf, "\r\n\r\n");
if ((strncmp(buf, "HTTP/", 5) == 0) && tmp_p) {
header_size = tmp_p - buf;
header_size += 4;
}
}
#endif
if (bufsize > 1500) {
fprintf(stderr, "warning: input HTML is over 1500bytes.\n");
}
//set_inputstr(buf, bufsize-1);
set_inputstr(buf+header_size, bufsize-1-header_size);
parse_html();
if (heap_used) free(buf);
return 0;
}
void *html2hdml_create_dir_mconfig(pool *p, char *dir) {
html2hdml_conf *cfg;
cfg = ap_pcalloc(p, sizeof(html2hdml_conf));
cfg->state = UNSET;
cfg->header = UNSET; // never set ON. harmful.
cfg->post = ON;
cfg->directory = ap_pstrdup(p,"/tmp");
cfg->types = ap_make_table(p, 8);
cfg->uris_ignore = ap_make_table(p, 8);
return (void *) cfg;
}
static void *html2hdml_merge_dir_mconfig(pool *p, void *origin, void *new) {
html2hdml_conf *cfg;
html2hdml_conf *cfg_origin = (html2hdml_conf *)origin;
html2hdml_conf *cfg_new = (html2hdml_conf *)new;
cfg = ap_pcalloc(p, sizeof(html2hdml_conf));
cfg->directory = ap_pstrdup(p,"/tmp");
cfg->types = ap_make_table(p, 8);
cfg->uris_ignore = ap_make_table(p, 8);
cfg->state = (cfg_new->state == UNSET) ? cfg_origin->state : cfg_new->state;
cfg->header = (cfg_new->header == UNSET) ? cfg_origin->header : cfg_new->header;
cfg->post = cfg_new->post;
if(strcmp(cfg_new->directory, "/tmp")){
cfg->directory = ap_pstrdup(p, cfg_new->directory);
} else if (strcmp(cfg_origin->directory, "/tmp")){
cfg->directory = ap_pstrdup(p, cfg_origin->directory);
}
cfg->types = ap_overlay_tables(p, cfg_new->types, cfg_origin->types);
cfg->uris_ignore = ap_overlay_tables(p, cfg_new->uris_ignore, cfg_origin->uris_ignore);
return (void *) cfg;
}
int check_table(const char *a) {
if (a == NULL)
return 0;
if('1' == a[0])
return 1;
return 0;
}
int table_find(const table * t, const char *key) {
array_header *hdrs_arr = ap_table_elts(t);
table_entry *elts = (table_entry *) hdrs_arr->elts;
int i;
if (key == NULL)
return 0;
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (!ap_fnmatch(elts[i].key, key, FNM_PATHNAME | FNM_CASE_BLIND))
if(check_table(elts[i].val))
return 1;
}
return 0;
}
static int call_main(request_rec *r, int assbackwards) {
int status = OK;
request_rec *subr;
//subr = (request_rec *) ap_sub_req_method_uri((char *) r->method, r->uri, r);
subr = (request_rec *) ap_sub_req_method_uri((char *) r->method, r->unparsed_uri, r);
subr->args = ap_pstrdup(subr->pool, r->args);
subr->assbackwards = assbackwards;
status = ap_run_sub_req(subr); // status != subr->status
ap_destroy_sub_req(subr);
ap_bflush(subr->connection->client);
//fprintf(stderr, "%s\n", r->uri);
//fprintf(stderr, "%d %d\n", status, subr->status);
//fprintf(stderr, "%s\n", subr->status_line);
{
int i, nelts;
table_entry *elts;
char *key;
elts =(table_entry *) ap_table_elts(subr->headers_out)->elts;
nelts = ap_table_elts(subr->headers_out)->nelts;
for (i = 0; i < nelts; i++) {
key = elts[i].key;
if (key && (strcmp(key, "Content-Length") != 0)) {
ap_table_set(r->headers_out, key, elts[i].val);
//fprintf(stderr, " %s: %s\n", elts[i].key, elts[i].val);
}
}
}
if (subr->status == 302) { // moved temporary
return subr->status;
} else {
return status;
}
}
static int call_container(request_rec *r, const char *uri, const char *html2hdmlcache, const char *content_length) {
int status = OK;
request_rec *subr;
subr = (request_rec *) ap_sub_req_method_uri("POST", uri, r);
subr->assbackwards = 0;
/*
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
*/
ap_table_set(subr->headers_in, "Content-Length", content_length);
ap_table_set(subr->subprocess_env, "HTML2HDML_SCRIPT_NAME", r->uri);
if(r->path_info)
ap_table_set(subr->subprocess_env, "HTML2HDML_INFO", r->path_info);
if(r->args)
ap_table_set(subr->subprocess_env, "HTML2HDML_QUERY_STRING", r->args);
ap_table_set(subr->subprocess_env, "HTML2HDML_CACHE", html2hdmlcache);
status = ap_run_sub_req(subr);
ap_destroy_sub_req(subr);
return status;
}
static int call_ssi(request_rec *r, const char *html2hdmlcache) {
int status = OK;
request_rec *subr;
subr = (request_rec *) ap_sub_req_method_uri("GET", html2hdmlcache, r);
subr->assbackwards = 0;
/*
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
*/
ap_table_set(subr->headers_in, "Content-Length", "0");
subr->filename = ap_pstrdup(subr->pool, html2hdmlcache);
subr->handler = ap_pstrdup(subr->pool, "server-parsed");
/* This really should only be needed if SSI is the last of the called handlers*/
subr->content_type = "text/html";
/* We fake it */
subr->finfo.st_mode = 1;
status = ap_run_sub_req(subr);
ap_destroy_sub_req(subr);
return status;
}
int html2hdml_fixup(request_rec *r) {
html2hdml_conf *cfg;
request_rec *subr;
char *type = NULL;
const char *handler = NULL;
int var_for_debug; // for deadline check
//fprintf(stderr, "fixup1\n");
#ifdef ZODIAX_WITH_HTML2HDML
cfg =
((zodiac_dir_config *)
ap_get_module_config(r->per_dir_config, &zodiac_module))->html2hdml_cfg;
#else
cfg = (html2hdml_conf *)
ap_get_module_config(r->per_dir_config, &html2hdml_module);
#endif
#ifdef ZODIAX_WITH_HTML2HDML
if (r->handler && (strcmp(r->handler, HTML2HDML_MOD_NAME) == 0))
r->handler = "zodiac-handler";
#endif
if (cfg->state < ON) {
return DECLINED;
}
if (r->main) {
return DECLINED;
}
/* If this is a HEAD only, we really don't need to involve ourselves. */
if (r->header_only) {
return DECLINED;
}
//fprintf(stderr, "fixup2\n");
/* So why switch to doing this? Somewhere since 1.3.6 something
has changed about the way that CGI's are done. Not sure what
it is, but this is now needed */
/* First, we check to see if this is SSI, mod_perl or cgi */
#if 0
if(r->handler) {
type = ap_pstrdup(r->pool, r->handler);
} else {
type = ap_pstrdup(r->pool, r->content_type);
}
if (handler = ap_table_get(cfg->types, type)){
if(strcmp(handler, "OFF")) {
ap_table_set(r->notes, "HTML2HDML_URI", handler);
} else {
return DECLINED;
}
} else {
return DECLINED;
}
if (table_find(cfg->uris_ignore, r->uri))
return DECLINED;
#endif
//fprintf(stderr, "fixup3\n");
/* #define EXPIRE_CHECK 1 */
var_for_debug = 1; // always OK
if (var_for_debug) {
int i;
table_entry *elts;
elts =(table_entry *) ap_table_elts(r->headers_in)->elts;
for (i = 0; i < ap_table_elts(r->headers_in)->nelts; ++i) {
if (elts[i].key != NULL) {
if(strcasecmp(elts[i].key,"accept")==0){
char* word = NULL;
char *p;
p = elts[i].val;
while(*p != '\0' && (word=ap_getword_nc(r->pool,&p,','))){
while(*word == ' ') word++;
if(strcasecmp(word,"text/x-hdml;version=2.0") == 0){
//OK MATCH
#ifdef ZODIAX_WITH_HTML2HDML
if (r->handler && (strcmp(r->handler, "zodiac-handler") == 0))
#endif
r->handler = HTML2HDML_MOD_NAME;
//fprintf(stderr, "fixup4 - %s\n", r->handler);
return DECLINED;
}
}
}
}
}
}
return DECLINED;
}
int html2hdml_handler(request_rec *r) {
int status=0;
int temp_fd, fd_out;
int pid;
int assbackwards;
char string[HUGE_STRING_LEN];
char *filename = NULL;
const char *handler = NULL;
const char *content_length = NULL;
html2hdml_conf *cfg;
struct stat sbuf;
//fprintf(stderr, "h2h_handler1\n");
if (r->main) {
return DECLINED;
}
//ap_table_setn(r->headers_out, "ModHtml2hdml", "1.1");
#ifdef ZODIAX_WITH_HTML2HDML
cfg =
((zodiac_dir_config *)
ap_get_module_config(r->per_dir_config, &zodiac_module))->html2hdml_cfg;
#else
cfg = (html2hdml_conf *)
ap_get_module_config(r->per_dir_config, &html2hdml_module);
#endif
/* Logic is reversed for assbackwards
One of these days I am going to ask why this
variable is named this.
*/
if (cfg->header == ON) {
assbackwards = 0;
} else {
assbackwards = 1;
}
pid = getpid();
filename = ap_psprintf(r->pool, "%s/.mod_html2hdml.%d", cfg->directory, pid);
ap_rflush(r);
if ((temp_fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,S_IRWXU)) < 0) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Bad mojo, mod_html2hdml couldn't create a file : %s",
filename);
return HTTP_INTERNAL_SERVER_ERROR;
}
fd_out = r->connection->client->fd;
r->connection->client->fd = temp_fd;
//fprintf(stderr, "h2h_handler2\n");
if((status = call_main(r, assbackwards)) != OK) {
r->connection->client->fd = fd_out;
return status;
}
//fprintf(stderr, "h2h_handler3\n");
//fprintf(stderr, "%d, %d\n", OK, status);
r->connection->client->fd = fd_out;
lseek(temp_fd, 0, SEEK_SET);
if(cfg->post == ON) {
if(fstat(temp_fd, &sbuf)) {
/* This would be very bad */
status = HTTP_INTERNAL_SERVER_ERROR;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, "fstat blew chunks in mod_html2hdml: %d", status);
return status;
}
content_length = ap_psprintf(r->pool, "%d", sbuf.st_size);
r->connection->client->fd_in = temp_fd;
} else {
content_length = ap_pstrdup(r->pool, "0");
}
/**/
{
FILE* fp;
html2hdml_conf *cfg;
int len;
//fprintf(stderr, "h2h_handler5\n");
//inlist = 0;
// method GET?
if (r->method_number != M_GET) {
r->allowed = M_GET;
return DECLINED;
}
//Directory configuration
#ifdef ZODIAX_WITH_HTML2HDML
cfg =
((zodiac_dir_config *)
ap_get_module_config(r->per_dir_config, &zodiac_module))->html2hdml_cfg;
#else
cfg = (html2hdml_conf *)
ap_get_module_config(r->per_dir_config, &html2hdml_module);
#endif
if (cfg == NULL)
return DECLINED;
//fprintf(stderr, "h2h_handler6\n");
//if (!cfg->enable)
if (cfg->state < ON)
return DECLINED;
#ifndef ZODIAX_WITH_HTML2HDML
// zodiax は実体がないんだから仕方ないよねえ。他の場合はどうすんだろ?
if (r->finfo.st_mode == 0)
return NOT_FOUND;
#endif
//fprintf(stderr, "h2h_handler7\n");
//isHDML=0;
//retrive accept
//fprintf(stderr, "hoge3\n");
//Open file
//fp = ap_pfopen (r->pool, filename, "rb");
fp = ap_pfopen (r->pool, filename, "rb");
if (fp == 0) {
ap_log_reason ("request file permissions deny", filename, r);
return FORBIDDEN;
}
r->content_type = "text/x-hdml;charset=Shift_JIS";
ap_soft_timeout ("send", r);
ap_send_http_header (r);
//fprintf(stderr, "h2h_handler8\n");
//html2hdml
html2hdml_convert(fp, r);
ap_rputs("", r);
ap_kill_timeout (r);
ap_pfclose (r->pool, fp);
}
/*
handler = ap_table_get(r->notes, "HTML2HDML_URI");
if(strcmp(handler, "SSIHTML2HDML")) {
if ((status = call_container(r, handler, filename,
content_length)) != OK) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r,
"The following error occured"
" while processing the html2hdml : %d", status);
return status;
}
} else {
if ((status = call_ssi(r, filename)) != OK) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r,
"The following error occured"
" while processing the html2hdml : %d", status);
return status;
}
}
*/
close(temp_fd);
unlink(filename);
return OK;
}
static const char *add_html2hdml(cmd_parms * cmd, void *mconfig, char *mime_type) {
html2hdml_conf *cfg = (html2hdml_conf *) mconfig;
ap_table_set(cfg->types, mime_type, "1");
return NULL;
}
static const char *ignore_uri(cmd_parms * cmd, void *mconfig, char *uri) {
html2hdml_conf *cfg = (html2hdml_conf *) mconfig;
ap_table_set(cfg->uris_ignore, uri, "1");
return NULL;
}
/* Dispatch list of content handlers */
static const handler_rec html2hdml_handlers[] = {
{ HTML2HDML_MOD_NAME, html2hdml_handler },
{ NULL, NULL }
};
static const command_rec html2hdml_cmds[] = {
{"Html2hdmlType", add_html2hdml, NULL, OR_ALL, TAKE1, "Takes two parameters, the mime type/handler and the uri to call on it."},
{"Html2hdml", ap_set_flag_slot, (void *) XtOffsetOf(html2hdml_conf, state), OR_ALL, FLAG, "This can either be On or Off (default it Off)."},
{"Html2hdmlHeader", ap_set_flag_slot, (void *) XtOffsetOf(html2hdml_conf, header), OR_ALL, FLAG, "This can either be On or Off (default it Off)."},
{"Html2hdmlPost", ap_set_flag_slot, (void *) XtOffsetOf(html2hdml_conf, post), OR_ALL, FLAG, "This can either be On or Off (default it On)."},
{"Html2hdmlCache", ap_set_string_slot, (void *) XtOffsetOf(html2hdml_conf, directory), OR_ALL, TAKE1, "Change the default directory from /tmp."},
{"Html2hdmlIgnore", ignore_uri, NULL, OR_ALL, TAKE1, "Change the default directory from /tmp."},
{NULL},
};
static void html2hdml_init(server_rec * s, pool * p) {
/* Tell apache we're here */
char ver[]=VERSION;
char result[100];
sprintf(result,"html2hdml/%s",ver);
ap_add_version_component(result);
}
/* Dispatch list for API hooks */
module MODULE_VAR_EXPORT html2hdml_module = {
STANDARD_MODULE_STUFF,
html2hdml_init, /* module initializer */
html2hdml_create_dir_mconfig, /* create per-dir config structures */
html2hdml_merge_dir_mconfig, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
html2hdml_cmds, /* table of config file commands */
html2hdml_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 */
html2hdml_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 */
#ifdef EAPI
,NULL, /* EAPI: add_module */
NULL, /* EAPI: remove_module */
NULL, /* EAPI: rewrite_command */
NULL /* EAPI: new_connection */
#endif
};