/* Copyright (C) 2007, WebThing Ltd. All rights reserved. * This is work in progress and does nothing useful. */ #include #include #include #include #include "mod_line_in.h" typedef struct { char lechar; } line_in_cfg; typedef struct { apr_bucket_brigade* bb; apr_bucket_brigade* bbin; int lechar; } line_in_ctx; #define line_in_filter_name "line-in" module AP_MODULE_DECLARE_DATA line_in_module; static apr_status_t line_in_filter(ap_filter_t *f, apr_bucket_brigade *bbout, ap_input_mode_t mode, apr_read_type_e block, apr_off_t nbytes) { apr_status_t rv = APR_SUCCESS ; apr_size_t bytes = 0 ; apr_size_t fbytes ; apr_size_t offs ; apr_bucket* b ; apr_bucket* b1 ; line_in_ctx* ctx; int count = 0; const char* buf; const char* le; char* fbuf; if (mode != AP_MODE_GETLINE) { ap_remove_input_filter(f); return ap_get_brigade(f->next, bbout, mode, block, nbytes) ; } ctx = (line_in_ctx*) f->ctx ; if (ctx == NULL) { line_in_cfg* cfg; cfg = ap_get_module_config(f->r->request_config, &line_in_module); if (cfg == NULL) { cfg = ap_get_module_config(f->r->per_dir_config, &line_in_module); } if (cfg->lechar == 0) { /* we're not configured */ ap_remove_input_filter(f); return ap_get_brigade(f->next, bbout, mode, block, nbytes) ; } f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(line_in_ctx)); ctx->bb = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc) ; ctx->bbin = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc) ; ctx->lechar = cfg->lechar; } rv = ap_get_brigade(f->next, ctx->bbin, mode, AP_MODE_READBYTES, nbytes) ; if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "Error in get_brigade"); return rv; } APR_BRIGADE_CONCAT(ctx->bb, ctx->bbin); while ( b != APR_BRIGADE_SENTINEL(ctx->bb) ) { if (!APR_BUCKET_IS_METADATA(b)) { rv = apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) ; if (rv != APR_SUCCESS) { return rv; } while (bytes > 0) { if (le = memchr(buf, ctx->lechar, bytes), le) { /* found a lineend in this bucket. */ offs = 1 + ((unsigned int)le-(unsigned int)buf) / sizeof(char) ; apr_bucket_split(b, offs) ; bytes -= offs ; buf += offs ; count += offs ; if (count > bytes) { return rv; /* got more data than the caller wants */ } b1 = APR_BUCKET_NEXT(b) ; APR_BUCKET_REMOVE(b); /* Is there any previous unterminated content ? */ if ( !APR_BRIGADE_EMPTY(ctx->bbin) ) { /* append this to any content waiting for a lineend */ APR_BRIGADE_INSERT_TAIL(ctx->bbin, b) ; rv = apr_brigade_pflatten(ctx->bbin, &fbuf, &fbytes, f->r->pool) ; /* make b a new bucket of the flattened stuff */ b = apr_bucket_pool_create(fbuf, fbytes, f->r->pool, f->r->connection->bucket_alloc) ; /* bbin has been consumed, so clear it */ apr_brigade_cleanup(ctx->bbin) ; } /* b now contains exactly one line */ APR_BRIGADE_INSERT_TAIL(bbout, b); b = b1 ; } else { /* no lineend found. Set aside any dangling content */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->bbin, b); bytes = 0 ; } } /* while bytes > 0 */ } else if (APR_BUCKET_IS_EOS(b)) { /* If there's data to pass, send it in one bucket */ if ( !APR_BRIGADE_EMPTY(ctx->bbin) ) { rv = apr_brigade_pflatten(ctx->bbin, &fbuf, &fbytes, f->r->pool) ; b1 = apr_bucket_pool_create(fbuf, fbytes, f->r->pool, f->r->connection->bucket_alloc) ; APR_BRIGADE_INSERT_TAIL(bbout, b1); } apr_brigade_cleanup(ctx->bbin) ; /* start again rather than segfault if a seriously buggy * filter in front of us sent a bogus EOS */ f->ctx = NULL ; /* move the EOS to the caller's brigade */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(bbout, b); } else { /* other metadata */ apr_bucket_delete(b); } /* OK, reset pointer to what's left (since we're not in a for-loop) */ b = APR_BRIGADE_FIRST(ctx->bb) ; } /* OK, now if there's still data in both bbin and bb, * we have to reconnect it. This is a faff, but should seldom happen. */ if (!APR_BRIGADE_EMPTY(ctx->bbin) && !APR_BRIGADE_EMPTY(ctx->bb)) { apr_bucket_brigade* bbtmp = ctx->bb; APR_BRIGADE_CONCAT(ctx->bbin, ctx->bb); ctx->bb = ctx->bbin; ctx->bbin = bbtmp; } return rv; } static void line_in_set_sep(request_rec* r, char ch) { line_in_cfg* cfg = ap_get_module_config(r->request_config, &line_in_module); if (cfg == NULL) { cfg = apr_palloc(r->pool, sizeof(line_in_cfg)) ; ap_set_module_config(r->request_config, &line_in_module, cfg); } cfg->lechar = ch; } static void line_in_hooks(apr_pool_t* pool) { ap_register_input_filter(line_in_filter_name, line_in_filter, NULL, AP_FTYPE_RESOURCE) ; APR_REGISTER_OPTIONAL_FN(line_in_set_sep) ; } static const char* line_in_sep(cmd_parms* cmd, void* CFG, const char* arg) { line_in_cfg* cfg = CFG; if (sscanf(arg, "%c", &cfg->lechar) != 1) return "Usage: InputSeparator sep" ; return NULL; } static const command_rec line_in_cmds[] = { AP_INIT_TAKE1("InputSeparator", line_in_sep, NULL, OR_ALL, "Separator char for parsing input") , { NULL } } ; static void* line_in_cr_cfg(apr_pool_t* pool, char* x) { line_in_cfg* ret = apr_pcalloc(pool, sizeof(line_in_cfg)); return ret; } static void* line_in_merge(apr_pool_t* pool, void* BASE, void* ADD) { line_in_cfg* base = BASE; line_in_cfg* add = ADD; line_in_cfg* ret = apr_palloc(pool, sizeof(line_in_cfg)); ret->lechar = (add->lechar == 0) ? base->lechar : add->lechar; return ret; } module AP_MODULE_DECLARE_DATA line_in_module = { STANDARD20_MODULE_STUFF, line_in_cr_cfg , line_in_merge , NULL , NULL , line_in_cmds , line_in_hooks };