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