/******************************************************************** * Copyright (c) 2006, Joachim Zobel * Author: Joachim Zobel * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id: mod_sqil.c 31 2009-09-29 20:32:31Z jo $ *********************************************************************/ /* mod_sqil is an output filter module that generates XML from database content based on the queries it gets as input. The input format is Structured Query Interface Language, a simple XML dialect that uses tags as column aliases and for bind variables. Its purpose is to generate XML using readable SQL. It can not generate arbitrarily formatted XML or HTML. It has one configuration option, e.g.: SQILDBDRecode iso-8859-1..xml This takes a recode request as its argument (see man recode) and recodes the database content accordingly using librecode. If you do not configure SQILDBDRecode the database content will be inserted between tags without any encoding. This module started as a rewrite of Nick Kews mod_sql. This is an xmlns namespace module and depends on mod_dbd mod_xmlns libapreq2 librecode mod_delay (opt.) Build: /usr/local/apache2/bin/apxs -c mod_sqil.c /usr/lib/librecode.la \ /usr/lib/libapreq2.la See SQIL.txt for a spec. of the tags and their attributes. */ #include "xmlns.h" #include #include #include #include #include #include #include #include #include #include #include #include "mod_delay.h" module AP_MODULE_DECLARE_DATA sqil_module; const xml_char_t *(*xmlns_get_attr_name) (const xmlns_attr_t * attrs, int attnum); const xml_char_t *(*xmlns_get_attr_val) (const xmlns_attr_t * attrs, int attnum); //int (*xmlns_get_attr_parsed)(const xmlns_attr_t* attrs, int attnum, parsedname* res); void (*xmlns_suppress_output) (xmlns_public *, int onoff); static void xmlns_funcs(void) { xmlns_get_attr_name = APR_RETRIEVE_OPTIONAL_FN(mod_xmlns_get_attr_name); xmlns_get_attr_val = APR_RETRIEVE_OPTIONAL_FN(mod_xmlns_get_attr_val); // xmlns_get_attr_parsed = APR_RETRIEVE_OPTIONAL_FN(mod_xmlns_get_attr_parsed); xmlns_suppress_output = APR_RETRIEVE_OPTIONAL_FN(mod_xmlns_suppress_output); } /* != DECLINED, OK, DONE */ // #define ABORT (-10) typedef enum { EVENT_START, EVENT_END } where_t; typedef enum { INIT, STATEMENT } what_t; typedef struct sqil_ctx sqil_ctx; typedef struct sqil_cfg sqil_cfg; typedef int (*sqil_handler_t) (xmlns_public *, sqil_ctx *, where_t); typedef struct sqil_fld sqil_fld; struct sqil_fld { const char *name; /* field name */ enum { OMIT, EMPTY } if_null; /* how to handle NULLs */ }; struct sqil_ctx { apr_array_header_t *st_atts; /* attributes stack */ apr_table_t *atts; /* atts to the current element */ apr_table_t *form; /* form vars (GET or POST) */ apr_table_t *parameters; /* squil parameters */ apr_table_t *statement; /* the current statements attr. */ apr_array_header_t *fields; /* squil fields for the current stmt. */ int in_statement; /* currently inside a statement tag */ int suppress_cnt; /* counts append supression */ /* sql stuff from driver */ apr_dbd_t *dbhdl; /* database handle */ const apr_dbd_driver_t *driver; /* driver functions */ // apr_dbd_transaction* trans ; /* current transaction */ /* sql errors */ int errnum; /* last SQL error */ const char *errfmt; /* format for reporting errors to client */ /* recode */ RECODE_OUTER rcd_out; RECODE_REQUEST rcd_req; /* working buffer stuff */ size_t offset; size_t avail; char *buf; }; /* sqil configuration */ struct sqil_cfg { const char *rcd_scan; }; /* SQL namespace */ static const char *XMLNS_SQIL = "http://heute-morgen.de/namespaces/sqil"; /*+ * Increases the size of the buffer held by ctx. * @param pool apr pool that is responsible for the buffer * @param ctx Home of the buffer * @param len Up to len is allocated additionally after offset */ #define BUFSZ 2048 static void sqil_alloc(apr_pool_t * pool, sqil_ctx * ctx, size_t len) { char *newbuf; if (len <= (ctx->avail - ctx->offset)) return; else while (len > (ctx->avail - ctx->offset)) ctx->avail += BUFSZ; newbuf = realloc(ctx->buf, ctx->avail); if (newbuf != ctx->buf) { if (ctx->buf) apr_pool_cleanup_kill(pool, ctx->buf, (void *) free); apr_pool_cleanup_register(pool, newbuf, (void *) free, apr_pool_cleanup_null); ctx->buf = newbuf; } } /*+ * Appends buf to the buffer held by ctx. * @param pool apr pool that is responsible for the buffer * @param ctx Home of the buffer * @param buf Is appended * @param len The length of the content in buf to be copied */ static void sqil_append(apr_pool_t * pool, sqil_ctx * ctx, const char *buf, size_t len) { if (!ctx->suppress_cnt) { sqil_alloc(pool, ctx, len); memcpy(ctx->buf + ctx->offset, buf, len); ctx->offset += len; } } /*+ * Ends all further (possibly resource intensive) statement tag processing. * @param ctx Home of the buffer */ static void sqil_abort(sqil_ctx * ctx) { ctx->suppress_cnt++; } /*+ * Returns the SQL string that has been build in buf. * @param sctx The sqil context * @return The buf buffer. */ static const char *sqil_get_string(apr_pool_t * pool, sqil_ctx * sctx) { // make shure 0 can safely be inserted sqil_alloc(pool, sctx, 1); // terminate buf *(sctx->buf + sctx->offset) = '\0'; return sctx->buf; } /*+ * Inititializes the recode infrastructure * @param r Request for logging * @param sctx Holds said infrastructure */ static void sqil_recode_init(request_rec * r, sqil_ctx * sctx) { sqil_cfg *cfg = ap_get_module_config(r->server->module_config, &sqil_module); sctx->rcd_out = NULL; sctx->rcd_req = NULL; if (!cfg) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Could not get module cofiguration."); return; } if (!cfg->rcd_scan) { /* no recoding configured */ return; } sctx->rcd_out = recode_new_outer(true); if (!sctx->rcd_out) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "recode_new_outer returned NULL."); return; } sctx->rcd_req = recode_new_request(sctx->rcd_out); if (!sctx->rcd_req) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "recode_new_request returned NULL."); return; } if (!recode_scan_request(sctx->rcd_req, cfg->rcd_scan)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "recode_scan_request failed for %s.", cfg->rcd_scan); return; } return; } /*+ * Deinititializes the recode infrastructure * @param r Request for logging * @param sctx Holds said infrastructure */ static const char *sqil_recode_deinit(request_rec * r, sqil_ctx * sctx) { if (sctx->rcd_req) { if (!recode_delete_request(sctx->rcd_req)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "recode_delete_request failed."); } } if (sctx->rcd_out) { if (!recode_delete_outer(sctx->rcd_out)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "recode_delete_outer failed."); } } } /*+ * Does the recoding. * @param r The request (for logging) * @param p The pool (for the return) * @param ctx Holds the recode infrastructure * @return The recoded string or a copy of str as a fallback. */ static char *sqil_recode(request_rec * r, apr_pool_t * p, sqil_ctx * sctx, const char *str) { char *rcd = NULL; char *rtn = NULL; if (sctx->rcd_req) { rcd = recode_string(sctx->rcd_req, str); if (rcd) { rtn = apr_pstrdup(p, rcd); free(rcd); } else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "recode_string failed for %s.", str); } } if (!rtn) { /* if recode is not working */ rtn = apr_pstrdup(p, str); } return rtn; } /*+ * sqil field handler. Puts the field name into the SQl * and adds it to the sctx->fields table. * @param ctxt the xmlns context * @param sctx the sqil context * @param where which kind of SAX event (start, end)? * @return OK */ static int sqil_field(xmlns_public * ctx, sqil_ctx * sctx, where_t where) { const char *name = NULL; const char *if_null = NULL; sqil_fld *fld = NULL; if (where == EVENT_START) { name = apr_table_get(sctx->atts, "name"); if (name) { sqil_append(ctx->f->r->pool, sctx, name, strlen(name)); fld = (sqil_fld *) apr_array_push(sctx->fields); fld->name = apr_pstrdup(ctx->f->r->pool, name); fld->if_null = OMIT; if_null = apr_table_get(sctx->atts, "null"); if (if_null && strcmp(if_null, "empty") == 0) { fld->if_null = EMPTY; } } else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ctx->f->r, "The name attribute is required for the field tag."); } } // nothing on EVENT_END return OK; } /*+ * sqil bind handler. We are actually not using bind variables * but put escaped parameters into the SQL string. * @param ctxt the xmlns context * @param sctx the sqil context * @param where which kind of SAX event (start, end)? * @return OK */ static int sqil_bind(xmlns_public * ctxt, sqil_ctx * sctx, where_t where) { const char *name = NULL; const char *value = NULL; if (where == EVENT_START) { name = apr_table_get(sctx->atts, "name"); value = apr_table_get(sctx->parameters, name); if (value) { value = apr_dbd_escape(sctx->driver, ctxt->f->r->pool, value, sctx->dbhdl); value = apr_pstrcat(ctxt->f->r->pool, "'", value, "'", NULL); } else { value = "NULL"; } sqil_append(ctxt->f->r->pool, sctx, value, strlen(value)); } // nothing on EVENT_END return OK; } /*+ * sqil if-(not-)exists handler. Conditionally (no parameter with name) * supresses output. * @param ctxt the xmlns context * @param sctx the sqil context * @param where which kind of SAX event (start, end)? * @param not !=0 for the not handler * @return OK */ static int sqil_exists(xmlns_public * ctx, sqil_ctx * sctx, where_t where, int not) { const char *name = apr_table_get(sctx->atts, "name"); const char *value = apr_table_get(sctx->parameters, name); if (!value && !not || value && not) { if (where == EVENT_START) { // supress append sctx->suppress_cnt++; } if (where == EVENT_END) { sctx->suppress_cnt--; } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, "Set suppress_cnt to %d for %s.", sctx->suppress_cnt, name); } return OK; } /*+ * sqil if-exists handler. * See sqil_exists for doc. */ static int sqil_if_exists(xmlns_public * ctx, sqil_ctx * sctx, where_t where) { return sqil_exists(ctx, sctx, where, 0); } /*+ * sqil if-not-exists handler. * See sqil_exists for doc. */ static int sqil_if_not_exists(xmlns_public * ctx, sqil_ctx * sctx, where_t where) { return sqil_exists(ctx, sctx, where, 1); } /*+ * sqil helper for xml escaping. * @param ctxt the xmlns context * @param will be written escaped */ #define FLUSH ap_fwrite(ctxt->f->next, ctxt->bb, (str+begin), (i-begin)) ; begin = i+1 #define BB ctxt->bb #define F ctxt->f->next static void sqil_xml_escape(xmlns_public * ctxt, const xml_char_t *str) { int begin, i; const int len = strlen(str); for ( begin=i=0; i' : FLUSH ; ap_fputs(F, BB, ">") ; break ; case '"' : FLUSH ; ap_fputs(F, BB, """) ; break ; default : break ; } } FLUSH ; } /*+ * sqil parameter handler. Fetches the named parameter and puts it * in the sctx->parameters table. * @param ctxt the xmlns context * @param sctx the sqil context * @param where which kind of SAX event (start, end)? * @return OK */ static int sqil_parameter(xmlns_public * ctxt, sqil_ctx * sctx, where_t where) { const char *type = NULL; const char *name = NULL; const char *value = NULL; if (where == EVENT_START) { type = apr_table_get(sctx->atts, "type"); if (!type) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->f->r, "The type attribute is required for the parameter tag."); return; } if (strcmp(type, "http-get") == 0) { if (sctx->form) { name = apr_table_get(sctx->atts, "name"); value = apr_table_get(sctx->form, name); apr_table_set(sctx->parameters, name, value); if (name && value) { ap_fputstrs(ctxt->f->next, ctxt->bb, "f->next, ctxt->bb, "\" value=\"", NULL); sqil_xml_escape(ctxt, value); ap_fputstrs(ctxt->f->next, ctxt->bb, "\" />", NULL); } } else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->f->r, "Tried to use an http parameter, but they are missing."); } } if (strcmp(type, "const") == 0) { name = apr_table_get(sctx->atts, "name"); value = apr_table_get(sctx->atts, "value"); apr_table_set(sctx->parameters, name, value); } } // nothing on EVENT_END return OK; } /*+ * Writes a tag with text and without attr. * @param ctxt The xmlns context * @param tag The tag * @param text The text inside the tag * @param atts The attributes as a string */ static void sqil_put_tag(xmlns_public * ctxt, const char *tag, const char *text, const char *atts) { const char *sep = " "; if (!atts) { atts = ""; sep = ""; } if (text) { ap_fputstrs(ctxt->f->next, ctxt->bb, "<", tag, sep, atts, ">", NULL); ap_fputs(ctxt->f->next, ctxt->bb, text); ap_fputstrs(ctxt->f->next, ctxt->bb, "", NULL); } else { ap_fputstrs(ctxt->f->next, ctxt->bb, "<", tag, sep, atts, "/>", NULL); } } /*+ *sqil handler for statement execution * @param ctxt the xmlns context * @param sctx the sqil context * @param where which kind of SAX event (start, end)? * @param what which tag to handle * @return OK, or abort if what==INIT encountered a 304 */ static int sqil_execute(xmlns_public * ctxt, sqil_ctx * sctx, where_t where, what_t what) { // START const char *val = NULL; // END int result; long rnum; int i; apr_dbd_results_t *results = NULL; apr_dbd_row_t *row = NULL; /* A pool that is cleared after each row. */ apr_pool_t *rwp = NULL; const char *query = NULL; const char *const errfmt = "Error %d executing \"%s\": %s"; const sqil_fld *flds = (sqil_fld *) (sctx->fields->elts); const char *errmsg = NULL; const char *col = NULL; const char *rowname = NULL; const char *headname = NULL; const char *rownum = NULL; const char *count = NULL; // 64 bit long fits char itoa_buf[21]; time_t mtime; void (*end_delay) (request_rec *); int rtn = OK; int status; int no_local_copy; request_rec *const r = ctxt->f->r; if (where == EVENT_START) { sctx->offset = 0; sctx->in_statement = 1; val = apr_table_get(sctx->atts, "rowname"); if (val) { apr_table_set(sctx->statement, "rowname", val); } val = apr_table_get(sctx->atts, "headname"); if (val) { apr_table_set(sctx->statement, "headname", val); } val = apr_table_get(sctx->atts, "rownum"); if (val) { apr_table_set(sctx->statement, "rownum", val); } val = apr_table_get(sctx->atts, "count"); if (val) { apr_table_set(sctx->statement, "count", val); } } if (where == EVENT_END) { query = sqil_get_string(r->pool, sctx); // Execute the statement result = apr_dbd_select(sctx->driver, r->pool, sctx->dbhdl, &results, query, // 0 would execute an async. query on pgsql. // This would not give an immediate error return. 1); if (result != 0) { // an error happened: log it and echo it sctx->errnum = result; errmsg = apr_dbd_error(sctx->driver, sctx->dbhdl, result); // sctx->driver->error(sctx->dbhdl, result) ; if (!errmsg) { errmsg = "(null)"; } ap_fprintf(ctxt->f->next, ctxt->bb, errfmt, result, query, errmsg); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, errfmt, result, query, errmsg); } else { rowname = apr_table_get(sctx->statement, "rowname"); headname = apr_table_get(sctx->statement, "headname"); rownum = apr_table_get(sctx->statement, "rownum"); count = apr_table_get(sctx->statement, "count"); if (headname) { // Write the headline ap_fputstrs(ctxt->f->next, ctxt->bb, "<", headname, ">", NULL); // First column is rownum if (rownum) { ap_fputstrs(ctxt->f->next, ctxt->bb, "<", rownum, "/>", NULL); } // Loop over the other columns for (i = 0; i < sctx->fields->nelts; i++) { ap_fputstrs(ctxt->f->next, ctxt->bb, "<", (flds + i)->name, "/>", NULL); } ap_fputstrs(ctxt->f->next, ctxt->bb, "\n", NULL); } /* Create the rowpool */ apr_pool_create(&rwp, r->pool); for (rnum = 0; apr_dbd_get_row(sctx->driver, r->pool, results, &row, -1) != -1; rnum++) { if (what == STATEMENT) { if (rowname) ap_fputstrs(ctxt->f->next, ctxt->bb, "<", rowname, ">", NULL); if (rownum) { sprintf(itoa_buf, "%d", (rnum + 1)); sqil_put_tag(ctxt, rownum, itoa_buf, NULL); } } // Loop over the columns for (i = 0; i < sctx->fields->nelts; i++) { col = apr_dbd_get_entry(sctx->driver, row, i); if (col) { /* mtime is special */ if (strcmp((flds + i)->name, "mtime") == 0) { mtime = atol(col); ap_update_mtime(r, apr_time_make(mtime, 0)); } if (what == STATEMENT) { sqil_put_tag(ctxt, (flds + i)->name, sqil_recode(r, rwp, sctx, col), NULL); } } // handle the "empty" attribute else if (what == STATEMENT && (flds + i)->if_null == EMPTY) { sqil_put_tag(ctxt, (flds + i)->name, NULL, "null=\"true\""); } } if ((what == STATEMENT) && rowname) { ap_fputstrs(ctxt->f->next, ctxt->bb, "\n", NULL); } /* Clear the rowpool */ apr_pool_clear(rwp); } if (what == STATEMENT) { sprintf(itoa_buf, "%d", rnum); if (count) { sqil_put_tag(ctxt, count, itoa_buf, NULL); } } } if (what == INIT) { ap_set_last_modified(r); /* We need to end delaying to enable calling ap_meets_conditions */ if (end_delay = APR_RETRIEVE_OPTIONAL_FN(delay_end), end_delay) { /* End of delaying */ end_delay(r); } /* We try to meet conditions, no matter if delaying works or not. */ status = ap_meets_conditions(r); if ( status != OK && status != DECLINED) { /* conditions where met */ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Aborting parse with status %d", status); /* Stop further processing of content */ sqil_abort(sctx); } else { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Conditions where not met."); } } // Cleanup apr_table_clear(sctx->statement); sctx->fields->nelts = 0; sctx->in_statement = 0; sctx->offset = 0; } return rtn; } /*+ *sqil statement handler * @param ctxt the xmlns context * @param sctx the sqil context * @param where which kind of SAX event (start, end)? * @return OK */ static int sqil_statement(xmlns_public * ctxt, sqil_ctx * sctx, where_t where) { return sqil_execute(ctxt, sctx, where, STATEMENT); } /*+ *sqil init handler * @param ctxt the xmlns context * @param sctx the sqil context * @param where which kind of SAX event (start, end)? * @return OK, or ABORT if a 304 should be send */ static int sqil_init(xmlns_public * ctxt, sqil_ctx * sctx, where_t where) { return sqil_execute(ctxt, sctx, where, INIT); } /*+ * Returns sqil handlers by tag name * @param name The tag name * @param sctx The length of the name * @return The handler for a given tag name */ //typedef void (*handler_t)(xmlns_public*, sqil_ctx*, where_t) ; static sqil_handler_t sqil_get_handler(const xml_char_t * name, int len) { static struct { const char *name; sqil_handler_t func; } handlers[] = { { "parameter", sqil_parameter}, { "init", sqil_init}, { "statement", sqil_statement}, { "bind", sqil_bind}, { "field", sqil_field}, { "if-exists", sqil_if_exists}, { "if-not-exists", sqil_if_not_exists}, /* shortcuts */ { "parm", sqil_parameter}, { "stmt", sqil_statement}, { "bnd", sqil_bind}, { "fld", sqil_field}, { "if-ex", sqil_if_exists}, { "if-nex", sqil_if_not_exists}, { NULL, NULL} }; int i; for (i = 0; handlers[i].name != NULL; ++i) { if (!strncmp(handlers[i].name, name, len)) { return handlers[i].func; } } return NULL; } /*+ * Copies attributes from xmlns into the atts table that is pushed onto * the st_atts stack. * @param r The request for the pool to allocate the atts table and logging. * @param sctx The usual sqil context (holding the attribute stack) * @param atts The attributes (from xmlns) * @return sctx->atts */ static apr_table_t *sqil_atts_push(request_rec * r, sqil_ctx * sctx, const xmlns_attr_t * atts) { const xml_char_t *attname; const xml_char_t *attval; apr_table_t **p_atts; int i; p_atts = (apr_table_t **) apr_array_push(sctx->st_atts); *p_atts = apr_table_make(r->pool, 6); sctx->atts = *p_atts; for (i = 0;; ++i) { attname = xmlns_get_attr_name(atts, i); if (!attname) break; attval = xmlns_get_attr_val(atts, i); apr_table_setn(sctx->atts, attname, attval); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Attribute %s=\"%s\"", attname, attval); } return sctx->atts; } /*+ * removes an atts table that was pushed onto * the st_atts stack. * @param sctx The usual sqil context (holding the attribute stack) * @return sctx->atts */ static apr_table_t *sqil_atts_pop(sqil_ctx * sctx) { apr_table_t **p_atts; p_atts = (apr_table_t **) apr_array_pop(sctx->st_atts); sctx->atts = *p_atts; return sctx->atts; } /*+ * sqil EndElement handler registered with xmlns. * @param ctxt The xmlns context * @param name3 The parsed tag name * @return the sqil handler s return value, if found or OK */ static int sqil_end(xmlns_public * ctx, const parsedname * name3) { sqil_ctx *sctx; sqil_handler_t handler; int rtn = OK; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, "Entered sqil_end"); if (sctx = ap_get_module_config(ctx->f->r->request_config, &sqil_module), sctx) { handler = sqil_get_handler(name3->elt, name3->eltlen); if (handler) { rtn = (*handler) (ctx, sctx, EVENT_END); sqil_atts_pop(sctx); } } else { ; /* error */ } return rtn; } /*+ * sqil StartElement handler registered with xmlns. * @param ctxt the xmlns context * @param name3 the parsed tag name * @param atts the attributes * @return passed from the sqil handler or OK */ static int sqil_start(xmlns_public * ctx, const parsedname * name3, const xmlns_attr_t * atts) { sqil_ctx *sctx; apr_table_t *(*form_vars) (request_rec *); sqil_handler_t handler = NULL; int rtn = OK; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, "Entered sqil_start"); if (sctx = ap_get_module_config(ctx->f->r->request_config, &sqil_module), sctx) { if (handler = sqil_get_handler(name3->elt, name3->eltlen), handler) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, "About to call handler for %s.", apr_pstrndup(ctx->f->r->pool, name3->elt, name3->eltlen)); sqil_atts_push(ctx->f->r, sctx, atts); rtn = (*handler) (ctx, sctx, EVENT_START); } else { /* no handler */ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, ctx->f->r, "No handler for %s.", apr_pstrndup(ctx->f->r->pool, name3->elt, name3->eltlen)); } } else { /* no sctx: error */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->f->r, "Error processing SQIL directive: no sctx found"); } return rtn; } /*+ * sqil character handler registered with xmlns. * @param ctxt the xmlns context * @param txt The string to handle * @param len The length of the string */ static int sqil_character(xmlns_public * ctx, const xml_char_t * txt, int len) { sqil_ctx *sctx = ap_get_module_config(ctx->f->r->request_config, &sqil_module); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, "Entered sqil_character"); if (sctx) { sqil_append(ctx->f->r->pool, sctx, txt, len); } return OK; } /*+ * sqil namespace initializer registered with xmlns. * @param ctxt the xmlns context * @param prefix not used * @param uri not used */ static void sqil_start_ns(xmlns_public * ctx, const xml_char_t * prefix, const xml_char_t * uri) { request_rec *r = ctx->f->r; // sctx will be initialized sqil_ctx *sctx; const char *arg; const char *errmsg; ap_dbd_t *dbd; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Entered sqil_start_ns for %s, %s", prefix, uri); sctx = apr_pcalloc(r->pool, sizeof(sqil_ctx)); // Read the request parameters sctx->form = apr_table_make(r->pool, 6); if (r->args && apreq_parse_query_string(r->pool, sctx->form, r->args) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Failed to parse query string."); sctx->form = NULL; } // set up attributes stack sctx->st_atts = apr_array_make(r->pool, 12, sizeof(apr_table_t *)); sctx->atts = NULL; // start with sqil_character enabled sctx->suppress_cnt = 0; // set up parameters and fields table sctx->parameters = apr_table_make(r->pool, 6); sctx->fields = apr_array_make(r->pool, 12, sizeof(sqil_fld)); // set up statement attr. table sctx->statement = apr_table_make(r->pool, 2); // we are not inside a statement yet sctx->in_statement = 0; // acquire dbd connection dbd = ap_dbd_acquire(r); if (dbd) { sctx->driver = dbd->driver; sctx->dbhdl = dbd->handle; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Successfully acquired connection."); } else { errmsg = "Error acquiring connection."; // error message for failed to acquire ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, errmsg); // ap_fprintf(ctx->f->next, ctx->bb, errmsg) ; return; } sqil_recode_init(r, sctx); ap_set_module_config(r->request_config, &sqil_module, sctx); // We remove the headers that provoke If- requests // since they are no longer true. // If there is an init section,they can be ecreated. apr_table_unset(r->headers_out, "Last-Modified"); apr_table_unset(r->headers_out, "ETag"); return; } /*+ * sqil namespace initializer registered with xmlns. * @param ctxt the xmlns context * @param prefix not used * @param uri not used */ static void sqil_end_ns(xmlns_public * ctx, const xml_char_t * ign) { sqil_ctx *sctx = ap_get_module_config(ctx->f->r->request_config, &sqil_module); if (sctx) { sqil_recode_deinit(ctx->f->r, sctx); } else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ctx->f->r, "Could not get sctx with ap_get_module_config - leaking memory."); } return; } /*+ * command handler for SQILDBDRecode * @param cmd - * @param cfg - The sqil_cfg structure. * @param arg - The Argument from the configuration. */ static const char *sqil_cmd_recode(cmd_parms * cmd, void *cfg, const char *arg) { sqil_cfg *srv_cfg = ap_get_module_config(cmd->server->module_config, &sqil_module); srv_cfg->rcd_scan = arg; return NULL; } /*+ * Initializes the sqil_cfg structure * @param pool - Pool with config. lifetime. * @param x - Ignored. * @return The initialized sqil_cfg struct. */ static void *cr_sqil_cfg(apr_pool_t * pool, server_rec * x) { sqil_cfg *cfg = apr_pcalloc(pool, sizeof(sqil_cfg)); cfg->rcd_scan = NULL; return cfg; } static const command_rec sqil_cmds[] = { AP_INIT_TAKE1("SQILDBDRecode", sqil_cmd_recode, NULL, RSRC_CONF, "String passed to recode for DB output charset conversion, eg. iso-8859-1..html4 ."), {NULL} }; static void sqil_hooks(apr_pool_t * pool) { static const xmlns xmlns_sqil = { XMLNS_VERSION, sqil_start, /* StartElement */ sqil_end, /* EndElement */ sqil_start_ns, /* StartNamespace */ sqil_end_ns, /* EndNamespace */ NULL, NULL, sqil_character, /* CharacterDataHandler */ sqil_character /* CdataHandler */ }; ap_register_provider(pool, "xmlns", XMLNS_SQIL, "1.0", &xmlns_sqil); ap_hook_optional_fn_retrieve(xmlns_funcs, NULL, NULL, APR_HOOK_MIDDLE); } module AP_MODULE_DECLARE_DATA sqil_module = { STANDARD20_MODULE_STUFF, NULL, NULL, cr_sqil_cfg, NULL, sqil_cmds, sqil_hooks };