/* Copyright (c) 2004, WebThing Ltd Author: Nick Kew This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Note to Users You are requested to register as a user, at http://apache.webthing.com/registration.html This entitles you to support from the developer. I'm unlikely to reply to help/support requests from non-registered users, unless you're paying and/or offering constructive feedback such as bug reports or sensible suggestions for further development. It also makes a small contribution to the effort that's gone into developing this work. */ #include #include #include #include #include #include #include #define BLOCKSIZE 256 extern apr_table_t* mod_upload_form(request_rec* r) ; module AP_MODULE_DECLARE_DATA csv_module ; typedef struct csv_cfg { apr_table_t* elts ; char* basepath ; char* pwd ; char* tmpl ; } csv_cfg ; static const char* macro_cmd(cmd_parms *cmd, void *cfg, const char* arg, const char* exp) { apr_table_set( ( (csv_cfg*)cfg ) -> elts, arg, exp ) ; return NULL ; } static const command_rec csv_cmds[] = { AP_INIT_TAKE1("CSVBaseURL", ap_set_string_slot, (void*) APR_OFFSETOF(csv_cfg, basepath), OR_ALL, "Base URL") , AP_INIT_TAKE1("CSVBasePath", ap_set_string_slot, (void*) APR_OFFSETOF(csv_cfg, pwd), OR_ALL, "Base Path") , AP_INIT_TAKE1("CSVTemplate", ap_set_string_slot, (void*) APR_OFFSETOF(csv_cfg, tmpl), OR_ALL, "Template File for CSV") , AP_INIT_TAKE2("CSVMacro", macro_cmd, NULL, OR_ALL, "Define an expansion macro") , {NULL} } ; static void* cr_csv_cfg(apr_pool_t* p, char* x) { csv_cfg* cfg = apr_pcalloc(p, sizeof(csv_cfg) ) ; cfg->elts = apr_table_make(p, 8) ; return cfg ; } //#define FLAGS APR_WRITE|APR_CREATE|APR_EXCL #define BUFSIZE 1024 #define FIELDSIZE 32 #define SPLITSIZE 32 static size_t splitline(char split[FIELDSIZE][SPLITSIZE], char line[]) { size_t field ; char* p = line ; for ( field = 0 ; field < SPLITSIZE ; ++field ) { char* e = strchr(p, ',') ; if ( e >= p ) { size_t len = e - p ; while ( len > FIELDSIZE-1) { strncpy(split[field++], p, FIELDSIZE-1) ; len -= FIELDSIZE-1 ; p += FIELDSIZE-1 ; split[field][FIELDSIZE-1] = 0 ; } strncpy(split[field], p, len ) ; split[field][len] = 0 ; p = e + 1 ; if ( ! *p ) break ; } else { size_t len = strlen(p) ; while ( len > FIELDSIZE-1) { strncpy(split[field++], p, FIELDSIZE-1) ; len -= FIELDSIZE-1 ; p += FIELDSIZE-1 ; } strcpy(split[field], p ) ; break ; } } split[++field][0] = 0 ; return field ; } static void keyworddispatch(csv_cfg* cfg, apr_file_t* out, char word[], char line[]) { //csv_cfg* cfg = ap_get_module_config (r->per_dir_config, &csv_module ) ; const char* fmt ; char* p = strchr(line, ',') ; if ( ! p ) return ; if ( fmt = apr_table_get(cfg->elts, word+1) , ! fmt ) return ; apr_file_printf(out, fmt, p+1) ; } static void trdispatch(apr_file_t* out, size_t sz, char split[][SPLITSIZE], int special) { size_t n = 0 ; if ( special ) apr_file_printf(out, "", split[0]+1) ; else apr_file_puts("", out) ; for ( n = 0 ; n < sz ; ++n ) { char token = split[n][0] ; switch ( token ) { case 0: apr_file_puts(" ", out) ; break ; case '!': apr_file_printf(out, "%s" , split[n]+1) ; break ; default: apr_file_printf(out, n==0 ? "%s" : "%s", //( special && n==0 ) ? " " : split[n]) ; split[n]) ; break ; } } apr_file_puts("", out) ; } static int csv(request_rec* r, const char* tmpnam) { csv_cfg* cfg = ap_get_module_config (r->per_dir_config, &csv_module ) ; char line[BUFSIZE] ; apr_table_t* formdata = mod_upload_form(r) ; apr_file_t* template ; apr_file_t* data ; apr_file_t* out ; char* fname ; char* pathname ; const char* page0 = apr_table_get(formdata, "page") ; const char* page = apr_psprintf(r->pool, "%s/%s", cfg->pwd, page0) ; const char* onam = apr_psprintf(r->pool, "%s.tmp", page) ; if ( apr_file_open(&data, tmpnam, APR_READ, APR_OS_DEFAULT, r->pool) != APR_SUCCESS ) { return HTTP_INTERNAL_SERVER_ERROR ; } apr_pool_cleanup_register(r->pool, data, (void*)apr_file_close, apr_pool_cleanup_null) ; if ( apr_file_open(&template, cfg->tmpl, APR_READ, APR_OS_DEFAULT, r->pool) != APR_SUCCESS ) { return HTTP_INTERNAL_SERVER_ERROR ; } apr_pool_cleanup_register(r->pool, template, (void*)apr_file_close, apr_pool_cleanup_null) ; if ( apr_file_open(&out, onam, APR_WRITE|APR_CREATE|APR_TRUNCATE, APR_OS_DEFAULT, r->pool) != APR_SUCCESS ) { return HTTP_INTERNAL_SERVER_ERROR ; } apr_pool_cleanup_register(r->pool, out, (void*)apr_file_close, apr_pool_cleanup_null) ; //state = before ; while ( apr_file_gets ( line, BUFSIZE, template ) == APR_SUCCESS ) { if ( strlen(line) < 2 ) apr_file_puts(line, out) ; else if ( ! strncmp(line, "<>", 8) ) { while ( apr_file_gets ( line, BUFSIZE, data ) == APR_SUCCESS ) { char split[FIELDSIZE][SPLITSIZE] ; size_t sz = splitline(split, line) ; switch ( line[0] ) { case '+': apr_file_puts("", out) ; break; case '-': apr_file_puts("
", out) ; break; case '=': keyworddispatch(cfg, out, split[0], line) ; break ; case ':': trdispatch(out, sz, split, 1) ; break ; case '\r': case '\n': break ; default: trdispatch(out, sz, split, 0) ; break ; } } } else { apr_file_puts(line, out) ; } } fname = apr_pstrcat(r->pool, page, ".html", NULL) ; apr_file_rename(onam, fname, r->pool) ; pathname = apr_psprintf(r->pool, "%s/%s.html", cfg->basepath, page0 ) ; apr_table_set(r->headers_out, "Content-Location", pathname) ; apr_table_set(r->headers_out, "Location", pathname) ; apr_table_unset(r->headers_out, "Content-Length") ; apr_table_unset(r->headers_out, "Content-Type") ; // r->status = HTTP_CREATED ; r->status = HTTP_TEMPORARY_REDIRECT ; // 307 return OK ; } static int csv_handler(request_rec* r) { const char* buf ; apr_size_t bytes ; apr_bucket_brigade* bb ; char* fname = 0 ; int status = 0 ; apr_bucket* b ; int end = 0 ; if ( ! r->handler || strcmp(r->handler, "csv" ) ) { return DECLINED ; } if ( r->method_number != M_POST ) return HTTP_METHOD_NOT_ALLOWED ; bb = apr_brigade_create(r->pool, r->connection->bucket_alloc) ; do { status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, BLOCKSIZE) ; if ( status == APR_SUCCESS ) for ( b = APR_BRIGADE_FIRST(bb) ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_NEXT(b) ) { if ( APR_BUCKET_IS_EOS(b) ) { end = 1 ; break ; } else if ( apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS ) { char* x = apr_pstrndup(r->pool, buf, bytes) ; if ( fname ) fname = apr_pstrcat(r->pool, fname, x, NULL) ; else fname = x ; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bucket read error") ; } } else ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Brigade error") ; apr_brigade_cleanup(bb) ; } while ( ! end && status == APR_SUCCESS ) ; apr_brigade_destroy(bb) ; return csv(r, fname) ; } static void csv_hooks(apr_pool_t *p) { ap_hook_handler(csv_handler, NULL, NULL, APR_HOOK_MIDDLE) ; } module AP_MODULE_DECLARE_DATA csv_module = { STANDARD20_MODULE_STUFF, cr_csv_cfg , NULL , NULL , NULL , csv_cmds , csv_hooks } ;