mod_csfilter
カッとなって作った。詳しくは後日書く。
ビルド等は
% apxs -c -I /usr/include/ClearSilver -l neo_cs -l neo_utl -l neo_cgi mod_csfilter.c % sudo apxs -i mod_csfilter.la
みたいな感じで。
#include "apr_strings.h" #include "ap_config.h" #include "httpd.h" #include "http_config.h" #include "http_protocol.h" #include "http_log.h" #include "http_main.h" #include "http_request.h" #include "mod_core.h" #include <ClearSilver.h> #define CSF_TPL_HANDLER_NAME "clearsilver" #define CSF_HDF_HANDLER_NAME "clearsilver-hdf" #define CSF_FILTER_NAME "CLEARSILVER" #define CSF_TEMPLATE_CONFIG_TAG "CSF_DefaultTemplate" #define CSF_TEMPLATE_HEADER_TAG "X-CSF-Template" #define CSF_TEMPLATE_ENV_TAG "CSF_TEMPLATE" /********************************************************************* ClearSilver *********************************************************************/ typedef struct { HDF *hdf; CSPARSE *cs; } cs_rec; typedef struct { apr_pool_t *pool; apr_bucket_brigade *bb; } cs_render_cb_rec; static NEOERR * csf_init_cs(cs_rec *csr) { NEOERR *err; csr->hdf = NULL; csr->cs = NULL; err = nerr_init(); if (err != STATUS_OK) return err; err = hdf_init(&csr->hdf); if (err != STATUS_OK) return err; err = cs_init(&csr->cs, csr->hdf); /* Template Filters from CGI Kit */ cs_register_strfunc(csr->cs, "url_escape", cgi_url_escape); cs_register_strfunc(csr->cs, "html_escape", cgi_html_escape_strfunc); cs_register_strfunc(csr->cs, "js_escape", cgi_js_escape); cs_register_strfunc(csr->cs, "text_html", cgi_text_html_strfunc); cs_register_strfunc(csr->cs, "html_strip", cgi_html_strip_strfunc); /* TODO: error check */ return err; } static void csf_final_cs(cs_rec *csr) { if (csr) { if (csr->cs) cs_destroy(&csr->cs); if (csr->hdf) hdf_destroy(&csr->hdf); } } static int csf_setup_env_callback(void *data, const char *key, const char *value) { HDF *hdf = (HDF *) data; NEOERR *err; err = hdf_set_value(hdf, key, value); return 1; } static NEOERR * csf_setup_cs(cs_rec *csr, request_rec *r) { NEOERR *err; HDF *hdf_env; err = hdf_get_node(csr->hdf, "ENV", &hdf_env); if (err != STATUS_OK) return err; apr_table_do(csf_setup_env_callback, hdf_env, r->subprocess_env, NULL); return STATUS_OK; } static NEOERR * csf_cs_render_callback(void *data, char *s) { cs_render_cb_rec *crr = data; apr_bucket *b; char *t; size_t len = strlen(s); if (! s || ! len) return STATUS_OK; t = apr_pmemdup(crr->pool, s, len); b = apr_bucket_pool_create(t, len, crr->pool, crr->bb->bucket_alloc); if (! b) return nerr_raise_errno(NERR_IO, "bucket allocation failed"); APR_BRIGADE_INSERT_TAIL(crr->bb, b); return STATUS_OK; } static void csf_cs_log_error(request_rec *r, NEOERR *err, const char *msg) { STRING es; string_init(&es); nerr_error_string(err, &es); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s%s%s", msg ? msg : "", msg ? ": " : "", es.buf); string_clear(&es); } static apr_status_t csf_cs_render_file(apr_bucket_brigade **prbb, request_rec *r, const char *template, const char *hdf_file, const char *hdf_str) { cs_rec ocsr; cs_render_cb_rec ocrr; NEOERR *err; apr_bucket_brigade *bb; apr_bucket *b; err = csf_init_cs(&ocsr); if (err != STATUS_OK) { csf_cs_log_error(r, err, "initialization of ClearSilver failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } /* load HDF */ if (hdf_file) { err = hdf_read_file(ocsr.hdf, hdf_file); if (err != STATUS_OK) { csf_cs_log_error(r, err, "loading HDF failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } } if (hdf_str) { err = hdf_read_string(ocsr.hdf, hdf_str); if (err != STATUS_OK) { csf_cs_log_error(r, err, "reading HDF failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } } err = csf_setup_cs(&ocsr, r); if (err != STATUS_OK) { csf_cs_log_error(r, err, "setup of ClearSilver failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } err = cs_parse_file(ocsr.cs, template); if (err != STATUS_OK) { csf_cs_log_error(r, err, "parsing of ClearSilver failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); /* TODO: error check */ ocrr.pool = r->pool; ocrr.bb = bb; err = cs_render(ocsr.cs, &ocrr, csf_cs_render_callback); if (err != STATUS_OK) { csf_cs_log_error(r, err, "rendering of ClearSilver failed"); apr_brigade_destroy(bb); csf_final_cs(&ocsr); return APR_EGENERAL; } csf_final_cs(&ocsr); b = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); *prbb = bb; return APR_SUCCESS; } /********************************************************************* Apache *********************************************************************/ module AP_MODULE_DECLARE_DATA csfilter_module; typedef struct { const char *default_template; } csf_dir_config; typedef struct { int seen_eos; apr_bucket_brigade *bb; const char *template_path; } csf_out_ctx_t; static apr_bucket_brigade * create_error_brigade(request_rec *r) { apr_bucket_brigade *bb; apr_bucket *b; bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); /* TODO: error handling */ /* custom message is not implemented in httpd-core yet */ b = ap_bucket_error_create(HTTP_INTERNAL_SERVER_ERROR, NULL, r->pool, r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); return bb; } static const char *get_template_path(request_rec *r) { csf_dir_config *conf = ap_get_module_config(r->per_dir_config, &csfilter_module); const char *template_path; request_rec *rr; do { /* from header */ template_path = apr_table_get(r->headers_out, CSF_TEMPLATE_HEADER_TAG); if (template_path) { apr_table_unset(r->headers_out, CSF_TEMPLATE_HEADER_TAG); break; } /* from header */ template_path = apr_table_get(r->err_headers_out, CSF_TEMPLATE_HEADER_TAG); if (template_path) { apr_table_unset(r->err_headers_out, CSF_TEMPLATE_HEADER_TAG); break; } /* from env */ template_path = apr_table_get(r->subprocess_env, CSF_TEMPLATE_ENV_TAG); if (template_path) break; /* from dir-config */ template_path = conf->default_template; } while (0); if (! template_path) return template_path; /* canonicalize relative path */ rr = ap_sub_req_lookup_file(template_path, r, NULL); if (rr && rr->status == HTTP_OK && rr->finfo.filetype != 0) { template_path = apr_pstrdup(r->pool, rr->filename); } else { /* TODO */ } if (rr) ap_destroy_sub_req(rr); return template_path; } /********************************************************************* Apache Handler *********************************************************************/ static int csf_handler_file_entity_check(request_rec *r) { apr_file_t *f = NULL; apr_status_t rv; ap_allow_standard_methods(r, MERGE_ALLOW, M_GET, M_POST, M_OPTIONS, -1); if (r->finfo.filetype == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "File does not exist: %s", r->filename); return HTTP_NOT_FOUND; } if ((rv = apr_file_open(&f, r->filename, APR_READ, APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "file permissions deny server access: %s", r->filename); return HTTP_FORBIDDEN; } ap_update_mtime(r, r->finfo.mtime); ap_set_last_modified(r); apr_file_close(f); ap_meets_conditions(r); return OK; } static int csf_tpl_handler(request_rec *r) { int ri; apr_status_t rv; apr_bucket_brigade *bb; if ((ri = csf_handler_file_entity_check(r)) != OK) return ri; if (r->header_only) return OK; rv = csf_cs_render_file(&bb, r, r->filename, NULL, NULL); if (rv != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; rv = ap_pass_brigade(r->output_filters, bb); if (rv != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; return OK; } static int csf_hdf_handler(request_rec *r) { int ri; apr_status_t rv; apr_bucket_brigade *bb; const char *template_path; if ((ri = csf_handler_file_entity_check(r)) != OK) return ri; template_path = get_template_path(r); if (! template_path) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "CSHandler: template file path is not specified"); return HTTP_INTERNAL_SERVER_ERROR; } if (r->header_only) return OK; rv = csf_cs_render_file(&bb, r, template_path, r->filename, NULL); if (rv != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; rv = ap_pass_brigade(r->output_filters, bb); if (rv != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; return OK; } static int csf_handler(request_rec *r) { if (! strcmp(r->handler, CSF_TPL_HANDLER_NAME)) return csf_tpl_handler(r); if (! strcmp(r->handler, CSF_HDF_HANDLER_NAME)) return csf_hdf_handler(r); return DECLINED; } /********************************************************************* Apache Filter *********************************************************************/ static apr_status_t csf_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; csf_out_ctx_t *ctx = f->ctx; apr_bucket *b; /* fast exit */ if (APR_BRIGADE_EMPTY(bb)) return ap_pass_brigade(f->next, bb); /* already done */ if (ctx && ctx->seen_eos) return ap_pass_brigade(f->next, bb); if (! ctx) { /* first time initialization */ const char *template_path = get_template_path(r); if (! template_path) { /* template path is not specified, so pass-thru */ ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx)); ctx->seen_eos = 0; ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); ctx->template_path = template_path; } b = APR_BRIGADE_FIRST(bb); while (b != APR_BRIGADE_SENTINEL(bb)) { apr_bucket *next_b = APR_BUCKET_NEXT(b); APR_BUCKET_REMOVE(b); /* remove from original brigade */ if (! ctx->seen_eos) { APR_BRIGADE_INSERT_TAIL(ctx->bb, b); } if (APR_BUCKET_IS_EOS(b)) ctx->seen_eos = 1; b = next_b; } /* given brigade to trash */ apr_brigade_destroy(bb); if (ctx->seen_eos) { /* reading stream completed */ char *hdf_str; apr_size_t len; apr_status_t rv; apr_bucket_brigade *out_bb; rv = apr_brigade_pflatten(ctx->bb, &hdf_str, &len, r->pool); apr_brigade_destroy(ctx->bb); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_brigade_pflatten() failed"); out_bb = create_error_brigade(r); return ap_pass_brigade(f->next, out_bb); } rv = csf_cs_render_file(&out_bb, r, ctx->template_path, NULL, hdf_str); if (rv != APR_SUCCESS) { out_bb = create_error_brigade(r); return ap_pass_brigade(f->next, out_bb); } /* forward brigade to next filter */ return ap_pass_brigade(f->next, out_bb); } return APR_SUCCESS; } /********************************************************************* Apache Module Config *********************************************************************/ static void *create_csf_dir_config(apr_pool_t *p, char *dummy) { csf_dir_config *result = apr_palloc(p, sizeof(csf_dir_config)); result->default_template = NULL; return result; } static const char *set_default_template(cmd_parms *cmd, void *mconfig, const char *msg) { csf_dir_config *conf = mconfig; conf->default_template = msg; return NULL; } static const command_rec csf_cmds[] = { AP_INIT_TAKE1(CSF_TEMPLATE_CONFIG_TAG, set_default_template, NULL, OR_ALL, "template file path"), {NULL}, }; static void register_hooks(apr_pool_t *p) { ap_hook_handler(csf_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_register_output_filter(CSF_FILTER_NAME, csf_out_filter, NULL, 1 ? AP_FTYPE_CONTENT_SET : AP_FTYPE_RESOURCE); } module AP_MODULE_DECLARE_DATA csfilter_module = { STANDARD20_MODULE_STUFF, create_csf_dir_config, /* create per-directory config structure */ NULL, /* merge per-directory config structures */ NULL, /* create per-server config structure */ NULL, /* merge per-server config structures */ csf_cmds, /* command apr_table_t */ register_hooks /* register hooks */ };
ライセンス表記ははずしてしまいましたが,Apache に準じます。