/* ** mod_layout.c -- Apache layout module */ #include "mod_layout.h" static void *create_dir_mconfig(apr_pool_t *p, char *dir) { /* So why -1, 0, and 1? You see, C lacks an arithmatic if. We need to know three states at any point. We need to know if something is unset, off or on. Hence we use these values. Apache already understands Off as 0 and 1 as on. */ layout_conf *cfg; cfg = ap_pcalloc(p, sizeof(layout_conf)); cfg->dir = ap_pstrdup(p, dir); cfg->replace_tags = UNSET; cfg->merge = UNSET; cfg->notes = UNSET; cfg->proxy = UNSET; cfg->comment = UNSET; cfg->append_header = UNSET; cfg->append_footer = UNSET; cfg->cache_needed = UNSET; cfg->display_origin = ON; cfg->header_enabled = UNSET; cfg->footer_enabled = UNSET; cfg->pattern_enabled = UNSET; cfg->http_header_enabled = UNSET; cfg->http_header = NULL; cfg->async_cache = LAYOUT_CACHE; cfg->begin_tag = LAYOUT_BEGINTAG; cfg->end_tag = LAYOUT_ENDTAG; cfg->async_post = OFF; cfg->time_format = LAYOUT_TIMEFORMAT; cfg->types = ap_make_table(p, 9); /* Doing default types was probably a bad idea */ cfg->uris_ignore = NULL; cfg->uris_ignore_header = NULL; cfg->uris_ignore_http_header = NULL; cfg->uris_ignore_footer = NULL; cfg->tag_ignore = NULL; cfg->tag_ignore_footer = NULL; cfg->tag_ignore_header = NULL; cfg->layouts = NULL; cfg->layout_html_handler = ON; ap_table_setn(cfg->types, INCLUDES_MAGIC_TYPE, "1"); ap_table_setn(cfg->types, INCLUDES_MAGIC_TYPE3, "1"); ap_table_setn(cfg->types, "server-parsed", "1"); ap_table_setn(cfg->types, "text/html", "1"); ap_table_setn(cfg->types, "text/plain", "1"); ap_table_setn(cfg->types, "perl-script", "1"); ap_table_setn(cfg->types, "cgi-script", "1"); ap_table_setn(cfg->types, "application/x-httpd-cgi", "1"); ap_table_setn(cfg->types, "application/x-httpd-php", "1"); ap_table_setn(cfg->types, "LayoutHandler application/x-httpd-php3", "1"); ap_table_setn(cfg->types, "LayoutHandler application/x-httpd-php3-source", "1"); return (void *) cfg; } static void *merge_dir_mconfig(apr_pool_t *p, void *origin, void *new) { layout_conf *cfg = ap_pcalloc(p, sizeof(layout_conf)); layout_conf *cfg_origin = (layout_conf *)origin; layout_conf *cfg_new = (layout_conf *)new; cfg->dir = ap_pstrdup(p, cfg_new->dir); cfg->replace_tags = UNSET; cfg->merge = UNSET; cfg->notes = UNSET; cfg->proxy = UNSET; cfg->comment = UNSET; cfg->append_header = UNSET; cfg->append_footer = UNSET; cfg->cache_needed = UNSET; cfg->display_origin = ON; cfg->header_enabled = UNSET; cfg->footer_enabled = UNSET; cfg->pattern_enabled = UNSET; cfg->http_header_enabled = UNSET; cfg->http_header = NULL; cfg->time_format = LAYOUT_TIMEFORMAT; cfg->async_post = OFF; cfg->async_cache = LAYOUT_CACHE; cfg->begin_tag = LAYOUT_BEGINTAG; cfg->end_tag = LAYOUT_ENDTAG; cfg->uris_ignore_header = NULL; cfg->uris_ignore_http_header = NULL; cfg->uris_ignore_footer = NULL; cfg->tag_ignore = NULL; cfg->tag_ignore_footer = NULL; cfg->tag_ignore_header = NULL; cfg->layouts = NULL; cfg->layout_html_handler = ON; if (strcmp(cfg_new->async_cache, LAYOUT_CACHE)){ cfg->async_cache = ap_pstrdup(p, cfg_new->async_cache); } else if (strcmp(cfg_origin->async_cache, LAYOUT_CACHE)){ cfg->async_cache = ap_pstrdup(p, cfg_origin->async_cache); } if (strcmp(cfg_new->time_format, LAYOUT_TIMEFORMAT)){ cfg->time_format = ap_pstrdup(p, cfg_new->time_format); } else if (strcmp(cfg_origin->time_format, LAYOUT_TIMEFORMAT)){ cfg->time_format = ap_pstrdup(p, cfg_origin->time_format); } if (strcmp(cfg_new->begin_tag, LAYOUT_BEGINTAG)){ cfg->begin_tag = ap_pstrdup(p, cfg_new->begin_tag); } else if (strcmp(cfg_origin->begin_tag, LAYOUT_BEGINTAG)){ cfg->begin_tag = ap_pstrdup(p, cfg_origin->begin_tag); } if (strcmp(cfg_new->end_tag, LAYOUT_ENDTAG)){ cfg->end_tag = ap_pstrdup(p, cfg_new->end_tag); } else if (strcmp(cfg_origin->end_tag, LAYOUT_ENDTAG)){ cfg->end_tag = ap_pstrdup(p, cfg_origin->end_tag); } cfg->layout_html_handler = (cfg_new->layout_html_handler == OFF) ? OFF : cfg_origin->layout_html_handler; cfg->cache_needed = (cfg_new->cache_needed == UNSET) ? cfg_origin->cache_needed : cfg_new->cache_needed; cfg->proxy = (cfg_new->proxy == UNSET) ? cfg_origin->proxy : cfg_new->proxy; cfg->merge = (cfg_new->merge == UNSET) ? cfg_origin->merge : cfg_new->merge; cfg->notes = (cfg_new->notes == UNSET) ? cfg_origin->notes : cfg_new->notes; cfg->replace_tags = (cfg_new->replace_tags == UNSET) ? cfg_origin->replace_tags : cfg_new->replace_tags; cfg->comment = (cfg_new->comment == UNSET) ? cfg_origin->comment : cfg_new->comment; cfg->async_post = (cfg_new->async_post == UNSET) ? cfg_origin->async_post : cfg_new->async_post; cfg->display_origin = cfg_new->display_origin; cfg->append_header = (cfg_new->append_header == UNSET) ? cfg_origin->append_header : cfg_new->append_header; cfg->append_footer = (cfg_new->append_footer == UNSET) ? cfg_origin->append_footer : cfg_new->append_footer; if (isOn(cfg->append_header) || isOn(cfg->append_footer)) { if (isOn(cfg->append_header) && isOn(cfg->append_footer)) { if (cfg_origin->layouts) cfg->layouts = ap_append_arrays(p, cfg_origin->layouts, cfg_new->layouts); else cfg->layouts = cfg_new->layouts; cfg->header_enabled = cfg_new->header_enabled ? cfg_new->header_enabled : cfg_origin->header_enabled; cfg->footer_enabled = cfg_new->footer_enabled ? cfg_new->footer_enabled : cfg_origin->header_enabled; } else if (isOn(cfg->append_header)) { cfg->header_enabled = cfg_new->header_enabled ? cfg_new->header_enabled : cfg_origin->header_enabled; cfg->footer_enabled = cfg_new->footer_enabled; cfg->layouts = layout_array_push_kind(p, cfg_origin->layouts, cfg_new->layouts, HEADER); /* We now assume just append_footer */ } else { cfg->header_enabled = cfg_new->header_enabled; cfg->footer_enabled = cfg_new->footer_enabled ? cfg_new->footer_enabled : cfg_origin->header_enabled; cfg->layouts = layout_array_push_kind(p, cfg_origin->layouts, cfg_new->layouts, FOOTER); } } else { if (cfg_new->layouts){ cfg->layouts = cfg_new->layouts; cfg->header_enabled = cfg_new->header_enabled; cfg->footer_enabled = cfg_new->footer_enabled; cfg->pattern_enabled = cfg_new->pattern_enabled; } else { cfg->layouts = cfg_origin->layouts; cfg->header_enabled = cfg_origin->header_enabled; cfg->footer_enabled = cfg_origin->footer_enabled; cfg->pattern_enabled = cfg_origin->pattern_enabled; } } if (cfg_new->http_header_enabled == UNSET){ cfg->http_header = ap_pstrdup(p, cfg_origin->http_header); cfg->http_header_enabled = cfg_origin->http_header_enabled; } else if (isOn(cfg_new->http_header_enabled)){ cfg->http_header = ap_pstrdup(p, cfg_new->http_header); cfg->http_header_enabled = cfg_new->http_header_enabled; } else { cfg->http_header_enabled = OFF; } /* This is pretty simple */ cfg->types = cfg_new->types; if (cfg_origin->uris_ignore) { if (cfg_new->uris_ignore) { cfg->uris_ignore = ap_overlay_tables(p, cfg_new->uris_ignore, cfg_origin->uris_ignore); } else { cfg->uris_ignore = cfg_origin->uris_ignore; } } else { cfg->uris_ignore = cfg_new->uris_ignore; } if (cfg_origin->uris_ignore_header) { if (cfg_new->uris_ignore_header) { cfg->uris_ignore_header = ap_overlay_tables(p, cfg_new->uris_ignore_header, cfg_origin->uris_ignore_header); } else { cfg->uris_ignore_header = cfg_origin->uris_ignore_header; } } else { cfg->uris_ignore_header = cfg_new->uris_ignore_header; } if (cfg_origin->uris_ignore_http_header) { if (cfg_new->uris_ignore_http_header) { cfg->uris_ignore_http_header = ap_overlay_tables(p, cfg_new->uris_ignore_http_header, cfg_origin->uris_ignore_http_header); } else { cfg->uris_ignore_http_header = cfg_origin->uris_ignore_http_header; } } else { cfg->uris_ignore_http_header = cfg_new->uris_ignore_http_header; } if (cfg_origin->uris_ignore_footer) { if (cfg_new->uris_ignore_footer) { cfg->uris_ignore_footer = ap_overlay_tables(p, cfg_new->uris_ignore_footer, cfg_origin->uris_ignore_footer); } else { cfg->uris_ignore_footer = cfg_origin->uris_ignore_footer; } } else { cfg->uris_ignore_footer = cfg_new->uris_ignore_footer; } if (cfg_origin->tag_ignore) { if (cfg_new->tag_ignore) { cfg->tag_ignore = ap_overlay_tables(p, cfg_new->tag_ignore, cfg_origin->tag_ignore); } else { cfg->tag_ignore = cfg_origin->tag_ignore; } } else { cfg->tag_ignore = cfg_new->tag_ignore; } if (cfg_origin->tag_ignore_footer) { if (cfg_new->tag_ignore_footer) { cfg->tag_ignore_footer = ap_overlay_tables(p, cfg_new->tag_ignore_footer, cfg_origin->tag_ignore_footer); } else { cfg->tag_ignore_footer = cfg_origin->tag_ignore_footer; } } else { cfg->tag_ignore_footer = cfg_new->tag_ignore_footer; } if (cfg_origin->tag_ignore_header) { if (cfg_new->tag_ignore_header) { cfg->tag_ignore_header = ap_overlay_tables(p, cfg_new->tag_ignore_header, cfg_origin->tag_ignore_header); } else { cfg->tag_ignore_header = cfg_origin->tag_ignore_header; } } else { cfg->tag_ignore_header = cfg_new->tag_ignore_header; } return (void *) cfg; } static apr_status_t layout_filter(ap_filter_t *f, apr_bucket_brigade *b) { request_rec *r = f->r; layout_filter_struct *ctx = f->ctx; apr_bucket *e; const char *str; apr_size_t len; layout_request *info = NULL; layout_conf *cfg; int start_position = 0; char *body = NULL; if (r->main) { return ap_pass_brigade(f->next, b); } ap_table_setn(r->headers_out, "X-Powered-By", "ModLayout/"VERSION); cfg = ap_get_module_config(r->per_dir_config, &layout_module); /* Now lets look at the ignore logic */ /* This is where we finally decide what is going to happen */ if (cfg->uris_ignore) { if (table_find(cfg->uris_ignore, r->uri)) { return ap_pass_brigade(f->next, b); } } info = create_layout_request(r, cfg); #ifdef DEBUG if (isOff(info->header) && isOff(info->footer) && isOff(info->http_header)) { return ap_pass_brigade(f->next, b); } printf("Fixup Header:%d: Footer:%d: HTTPHeader:%d:\n", info->header, info->footer, info->http_header); #endif if (ctx == NULL) { f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); ctx->b = apr_brigade_create(f->r->pool, r->connection->bucket_alloc); ctx->output = NULL; } apr_table_unset(f->r->headers_out, "Content-Length"); apr_table_unset(f->r->headers_out, "ETag"); APR_BRIGADE_FOREACH(e, b) { if (APR_BUCKET_IS_EOS(e) || APR_BUCKET_IS_FLUSH (e)) { info->f = f->next; info->b = ctx->b; if (isOn(info->origin)) { /* This code right here could be replaced and this would all be faster */ /* We have to make two passes through to make sure the headers always happen */ if (info->header && (string_search(r, ctx->output, cfg->begin_tag, start_position, 0) == -1)) { layout_kind(r, cfg, info, HEADER); } parser_put(r, cfg, info, ctx->output, start_position); if (info->footer && (string_search(r, ctx->output, cfg->end_tag, start_position, 0) == -1)) { layout_kind(r, cfg, info, FOOTER); } } else { layout_kind(r, cfg, info, HEADER); if (isOn(cfg->notes)) update_info(r->notes, info); layout_kind(r, cfg, info, LAYOUT); if (isOn(cfg->notes)) update_info(r->notes, info); layout_kind(r, cfg, info, FOOTER); } APR_BUCKET_REMOVE(e); APR_BRIGADE_INSERT_TAIL(ctx->b, e); return ap_pass_brigade(f->next, ctx->b); } if (APR_BUCKET_IS_FLUSH(e)) { continue; } apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ); if (ctx->output) { ctx->output = apr_pstrcat(r->pool, ctx->output, str, NULL); } else { ctx->output = apr_pstrcat(r->pool, str, NULL); } } apr_brigade_destroy(b); return APR_SUCCESS; } static const char * add_http_header(cmd_parms * cmd, void *mconfig, const char *uri) { layout_conf *cfg = (layout_conf *) mconfig; cfg->http_header = ap_pstrdup(cmd->pool, uri); cfg->http_header_enabled = ON; return NULL; } static const char * add_layout_pattern(cmd_parms * cmd, void *mconfig, const char *pattern, const char *layout, const char *mode) { layout_conf *cfg = (layout_conf *) mconfig; struct stat sbuf; const char *temp; layout_string *directive; directive = ap_pcalloc (cmd->pool, sizeof (layout_string)); if (ap_ind(layout, ' ') != -1) { directive->comment = ap_pstrdup (cmd->pool, "Static Content"); directive->string = ap_pstrdup (cmd->pool, layout); directive->type = 1; } else if (stat(layout, &sbuf) == 0){ unless((temp = layout_add_file(cmd, layout))) { return NULL; }; directive->comment = ap_pstrdup (cmd->pool, layout); directive->string = ap_pstrdup (cmd->pool, temp); directive->type = 1; } else { directive->comment = ap_pstrdup (cmd->pool, layout); directive->string = ap_pstrdup (cmd->pool, layout); directive->type = 0; if (cfg->cache_needed == UNSET) cfg->cache_needed = ON; } directive->pattern = ap_pstrdup (cmd->pool, pattern); unless(cfg->layouts) { cfg->layouts = ap_make_array (cmd->pool, 1, sizeof (layout_string *)); } unless(strcasecmp(cmd->cmd->name, "LayoutHeader")) { cfg->header_enabled = ON; directive->kind = HEADER; } else unless(strcasecmp(cmd->cmd->name, "LayoutFooter")) { cfg->footer_enabled = ON; directive->kind = FOOTER; } else { cfg->pattern_enabled = ON; directive->kind = LAYOUT; } if (mode) { unless(strcasecmp(mode, "append")) { directive->append = APPEND; } else unless(strcasecmp(mode, "prepend")) { directive->append = PREPEND; } else unless(strcasecmp(mode, "replace")) { directive->append = REPLACE; } else { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, "The type %s is not valid for %s ",mode, cmd->cmd->name); directive->append = REPLACE; } } else { directive->append = REPLACE; } *(layout_string **) ap_push_array (cfg->layouts) = (layout_string *) directive; return NULL; } static const char * add_layout(cmd_parms * cmd, void *mconfig, const char *layout) { layout_conf *cfg = (layout_conf *) mconfig; unless(strcasecmp(cmd->cmd->name, "LayoutHeader")) { (void)add_layout_pattern(cmd, mconfig, (char *)cfg->begin_tag, layout, "append"); } else unless(strcasecmp(cmd->cmd->name, "LayoutFooter")) { (void)add_layout_pattern(cmd, mconfig, (char *)cfg->end_tag, layout, "prepend"); } return NULL; } static const char * ignore_uri(cmd_parms * cmd, void *mconfig, const char *uri) { layout_conf *cfg = (layout_conf *) mconfig; unless(cfg->uris_ignore) cfg->uris_ignore = ap_make_table(cmd->pool, 1); ap_table_setn(cfg->uris_ignore, uri, "1"); return NULL; } static const char * ignore_header_uri(cmd_parms * cmd, void *mconfig, const char *uri) { layout_conf *cfg = (layout_conf *) mconfig; unless(cfg->uris_ignore_header) cfg->uris_ignore_header = ap_make_table(cmd->pool, 1); ap_table_setn(cfg->uris_ignore_header, uri, "1"); return NULL; } static const char * ignore_http_header_uri(cmd_parms * cmd, void *mconfig, const char *uri) { layout_conf *cfg = (layout_conf *) mconfig; unless(cfg->uris_ignore_http_header) cfg->uris_ignore_http_header = ap_make_table(cmd->pool, 1); ap_table_setn(cfg->uris_ignore_http_header, uri, "1"); return NULL; } static const char * ignore_footer_uri(cmd_parms * cmd, void *mconfig, const char *uri) { layout_conf *cfg = (layout_conf *) mconfig; unless(cfg->uris_ignore_footer) cfg->uris_ignore_footer = ap_make_table(cmd->pool, 1); ap_table_setn(cfg->uris_ignore_footer, uri, "1"); return NULL; } static const char * add_type(cmd_parms * cmd, void *mconfig, const char *type) { layout_conf *cfg = (layout_conf *) mconfig; ap_table_setn(cfg->types, type, "1"); return NULL; } static const char * tag_ignore_add(cmd_parms * cmd, void *mconfig, const char *type) { layout_conf *cfg = (layout_conf *) mconfig; unless(cfg->tag_ignore) cfg->tag_ignore = ap_make_table(cmd->pool, 1); ap_table_setn(cfg->tag_ignore, type, "1"); return NULL; } static const char * tag_ignore_footer_add(cmd_parms * cmd, void *mconfig, const char *type) { layout_conf *cfg = (layout_conf *) mconfig; unless(cfg->tag_ignore_footer) cfg->tag_ignore_footer = ap_make_table(cmd->pool, 1); ap_table_setn(cfg->tag_ignore_footer, type, "1"); return NULL; } static const char * tag_ignore_header_add(cmd_parms * cmd, void *mconfig, const char *type) { layout_conf *cfg = (layout_conf *) mconfig; unless(cfg->tag_ignore_header) cfg->tag_ignore_header = ap_make_table(cmd->pool, 1); ap_table_setn(cfg->tag_ignore_header, type, "1"); return NULL; } static const char * merge_add(cmd_parms * cmd, void *mconfig, int flag) { layout_conf *cfg = (layout_conf *) mconfig; cfg->merge = flag; cfg->display_origin = ON; return NULL; } static const char * footer_off(cmd_parms * cmd, void *mconfig) { layout_conf *cfg = (layout_conf *) mconfig; cfg->footer_enabled = OFF; return NULL; } static const char * header_off(cmd_parms * cmd, void *mconfig) { layout_conf *cfg = (layout_conf *) mconfig; cfg->header_enabled = OFF; return NULL; } static const char * http_header_off(cmd_parms * cmd, void *mconfig) { layout_conf *cfg = (layout_conf *) mconfig; cfg->http_header = NULL; cfg->http_header_enabled = OFF; return NULL; } static const char * append_layouts(cmd_parms * cmd, void *mconfig, int flag) { layout_conf *cfg = (layout_conf *) mconfig; cfg->append_header = flag; cfg->append_footer = flag; return NULL; } static void layout_register_hooks(apr_pool_t *p) { ap_register_output_filter("LAYOUT", layout_filter, NULL, AP_FTYPE_CONTENT_SET); } static const command_rec layout_cmds[] = { AP_INIT_TAKE23("Layout", add_layout_pattern, NULL, OR_ALL, Layout), AP_INIT_TAKE1("LayoutHeader", add_layout, NULL, OR_ALL, LayoutHeader), AP_INIT_TAKE1("LayoutFooter", add_layout, NULL, OR_ALL, LayoutFooter), AP_INIT_FLAG("LayoutAppend", append_layouts, NULL, OR_ALL, LayoutAppend), AP_INIT_FLAG("LayoutAppendHeader", ap_set_flag_slot, (void *) APR_XtOffsetOf(layout_conf, append_header), OR_ALL, LayoutHeaderAppend), AP_INIT_FLAG("LayoutAppendFooter", ap_set_flag_slot, (void *) APR_XtOffsetOf(layout_conf, append_header), OR_ALL, LayoutFooterAppend), AP_INIT_TAKE1("LayoutIgnoreURI", ignore_uri, NULL, OR_ALL, LayoutIgnoreURI), AP_INIT_TAKE1("LayoutIgnoreHeaderURI", ignore_header_uri, NULL, OR_ALL, LayoutIgnoreHeaderURI), AP_INIT_TAKE1("LayoutIgnoreHTTPHeaderURI", ignore_http_header_uri, NULL, OR_ALL, LayoutIgnoreHTTPHeaderURI), AP_INIT_TAKE1("LayoutIgnoreFooterURI", ignore_footer_uri, NULL, OR_ALL, LayoutIgnoreFooterURI), AP_INIT_FLAG("LayoutComment", ap_set_flag_slot, (void *) APR_XtOffsetOf(layout_conf, comment), OR_ALL, LayoutComment), AP_INIT_FLAG("LayoutDisplayOriginal", ap_set_flag_slot, (void *) APR_XtOffsetOf(layout_conf, display_origin), OR_ALL, LayoutDisplayOriginal), AP_INIT_TAKE1("LayoutTimeFormat", ap_set_string_slot, (void *) APR_XtOffsetOf(layout_conf, time_format), OR_ALL, LayoutTimeFormat), AP_INIT_TAKE1("LayoutHTTPHeader", add_http_header, NULL, OR_ALL, LayoutHTTPHeader), AP_INIT_TAKE1("LayoutIgnoreTag", tag_ignore_add, NULL, OR_ALL, LayoutIgnoreTag), AP_INIT_TAKE1("LayoutIgnoreTagFooter", tag_ignore_footer_add, NULL, OR_ALL, LayoutIgnoreTagFooter), AP_INIT_TAKE1("LayoutIgnoreTagHeader", tag_ignore_header_add, NULL, OR_ALL, LayoutIgnoreTagHeader), AP_INIT_FLAG("LayoutPostAsync", ap_set_flag_slot, (void *) APR_XtOffsetOf(layout_conf, async_post), OR_ALL, LayoutPostAsync), AP_INIT_TAKE1("LayoutBeginTag", ap_set_string_slot, (void *) APR_XtOffsetOf(layout_conf, begin_tag), OR_ALL, LayoutMergeBeginTag), AP_INIT_TAKE1("LayoutEndTag", ap_set_string_slot, (void *) APR_XtOffsetOf(layout_conf, end_tag), OR_ALL, LayoutMergeEndTag), AP_INIT_FLAG("LayoutReplaceTags", ap_set_flag_slot, (void *) APR_XtOffsetOf(layout_conf, replace_tags), OR_ALL, LayoutReplaceTags), AP_INIT_FLAG("LayoutNotes", ap_set_flag_slot, (void *) APR_XtOffsetOf(layout_conf, notes), OR_ALL, LayoutNotes), {NULL} }; /* Dispatch list for API hooks */ module AP_MODULE_DECLARE_DATA layout_module = { STANDARD20_MODULE_STUFF, create_dir_mconfig, /* create per-dir config structures */ merge_dir_mconfig, /* 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_register_hooks /* register hooks */ };